Index: stable/12/sys/amd64/linux/linux.h =================================================================== --- stable/12/sys/amd64/linux/linux.h (revision 360445) +++ stable/12/sys/amd64/linux/linux.h (revision 360446) @@ -1,463 +1,457 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2013 Dmitry Chagin * Copyright (c) 1994-1996 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _AMD64_LINUX_H_ #define _AMD64_LINUX_H_ +#include + #include #include #define LINUX_LEGACY_SYSCALLS /* * debugging support */ extern u_char linux_debug_map[]; #define ldebug(name) isclr(linux_debug_map, LINUX_SYS_linux_ ## name) #define ARGS(nm, fmt) "linux(%ld/%ld): "#nm"("fmt")\n", \ (long)td->td_proc->p_pid, (long)td->td_tid #define LMSG(fmt) "linux(%ld/%ld): "fmt"\n", \ (long)td->td_proc->p_pid, (long)td->td_tid #define LINUX_DTRACE linuxulator - -#define PTRIN(v) (void *)(v) -#define PTROUT(v) (uintptr_t)(v) - -#define CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0) -#define CP2(src,dst,sfld,dfld) do { (dst).dfld = (src).sfld; } while (0) -#define PTRIN_CP(src,dst,fld) \ - do { (dst).fld = PTRIN((src).fld); } while (0) /* * Provide a separate set of types for the Linux types. */ typedef int32_t l_int; typedef int64_t l_long; typedef int16_t l_short; typedef uint32_t l_uint; typedef uint64_t l_ulong; typedef uint16_t l_ushort; typedef l_ulong l_uintptr_t; typedef l_long l_clock_t; typedef l_int l_daddr_t; typedef l_ulong l_dev_t; typedef l_uint l_gid_t; typedef l_ushort l_gid16_t; typedef l_uint l_uid_t; typedef l_ushort l_uid16_t; typedef l_ulong l_ino_t; typedef l_int l_key_t; typedef l_long l_loff_t; typedef l_uint l_mode_t; typedef l_long l_off_t; typedef l_int l_pid_t; typedef l_ulong l_size_t; typedef l_long l_ssize_t; typedef l_long l_suseconds_t; typedef l_long l_time_t; typedef l_int l_timer_t; typedef l_int l_mqd_t; typedef l_size_t l_socklen_t; typedef l_ulong l_fd_mask; typedef struct { l_int val[2]; } l_fsid_t; typedef struct { l_time_t tv_sec; l_suseconds_t tv_usec; } l_timeval; #define l_fd_set fd_set /* * Miscellaneous */ #define LINUX_AT_COUNT 19 /* Count of used aux entry types. */ struct l___sysctl_args { l_uintptr_t name; l_int nlen; l_uintptr_t oldval; l_uintptr_t oldlenp; l_uintptr_t newval; l_size_t newlen; l_ulong __spare[4]; }; /* Resource limits */ #define LINUX_RLIMIT_CPU 0 #define LINUX_RLIMIT_FSIZE 1 #define LINUX_RLIMIT_DATA 2 #define LINUX_RLIMIT_STACK 3 #define LINUX_RLIMIT_CORE 4 #define LINUX_RLIMIT_RSS 5 #define LINUX_RLIMIT_NPROC 6 #define LINUX_RLIMIT_NOFILE 7 #define LINUX_RLIMIT_MEMLOCK 8 #define LINUX_RLIMIT_AS 9 /* Address space limit */ #define LINUX_RLIM_NLIMITS 10 struct l_rlimit { l_ulong rlim_cur; l_ulong rlim_max; }; /* * stat family of syscalls */ struct l_timespec { l_time_t tv_sec; l_long tv_nsec; }; struct l_newstat { l_dev_t st_dev; l_ino_t st_ino; l_ulong st_nlink; l_uint st_mode; l_uid_t st_uid; l_gid_t st_gid; l_uint __st_pad1; l_dev_t st_rdev; l_off_t st_size; l_long st_blksize; l_long st_blocks; struct l_timespec st_atim; struct l_timespec st_mtim; struct l_timespec st_ctim; l_long __unused1; l_long __unused2; l_long __unused3; }; /* sigaction flags */ #define LINUX_SA_NOCLDSTOP 0x00000001 #define LINUX_SA_NOCLDWAIT 0x00000002 #define LINUX_SA_SIGINFO 0x00000004 #define LINUX_SA_RESTORER 0x04000000 #define LINUX_SA_ONSTACK 0x08000000 #define LINUX_SA_RESTART 0x10000000 #define LINUX_SA_INTERRUPT 0x20000000 #define LINUX_SA_NOMASK 0x40000000 #define LINUX_SA_ONESHOT 0x80000000 /* sigprocmask actions */ #define LINUX_SIG_BLOCK 0 #define LINUX_SIG_UNBLOCK 1 #define LINUX_SIG_SETMASK 2 /* sigaltstack */ #define LINUX_MINSIGSTKSZ 2048 typedef void (*l_handler_t)(l_int); typedef struct { l_handler_t lsa_handler; l_ulong lsa_flags; l_uintptr_t lsa_restorer; l_sigset_t lsa_mask; } l_sigaction_t; typedef struct { l_uintptr_t ss_sp; l_int ss_flags; l_size_t ss_size; } l_stack_t; struct l_fpstate { u_int16_t cwd; u_int16_t swd; u_int16_t twd; u_int16_t fop; u_int64_t rip; u_int64_t rdp; u_int32_t mxcsr; u_int32_t mxcsr_mask; u_int32_t st_space[32]; u_int32_t xmm_space[64]; u_int32_t reserved2[24]; }; struct l_sigcontext { l_ulong sc_r8; l_ulong sc_r9; l_ulong sc_r10; l_ulong sc_r11; l_ulong sc_r12; l_ulong sc_r13; l_ulong sc_r14; l_ulong sc_r15; l_ulong sc_rdi; l_ulong sc_rsi; l_ulong sc_rbp; l_ulong sc_rbx; l_ulong sc_rdx; l_ulong sc_rax; l_ulong sc_rcx; l_ulong sc_rsp; l_ulong sc_rip; l_ulong sc_rflags; l_ushort sc_cs; l_ushort sc_gs; l_ushort sc_fs; l_ushort sc___pad0; l_ulong sc_err; l_ulong sc_trapno; l_sigset_t sc_mask; l_ulong sc_cr2; struct l_fpstate *sc_fpstate; l_ulong sc_reserved1[8]; }; struct l_ucontext { l_ulong uc_flags; l_uintptr_t uc_link; l_stack_t uc_stack; struct l_sigcontext uc_mcontext; l_sigset_t uc_sigmask; }; #define LINUX_SI_PREAMBLE_SIZE (4 * sizeof(int)) #define LINUX_SI_MAX_SIZE 128 #define LINUX_SI_PAD_SIZE ((LINUX_SI_MAX_SIZE - \ LINUX_SI_PREAMBLE_SIZE) / sizeof(l_int)) typedef union l_sigval { l_int sival_int; l_uintptr_t sival_ptr; } l_sigval_t; typedef struct l_siginfo { l_int lsi_signo; l_int lsi_errno; l_int lsi_code; union { l_int _pad[LINUX_SI_PAD_SIZE]; struct { l_pid_t _pid; l_uid_t _uid; } _kill; struct { l_timer_t _tid; l_int _overrun; char _pad[sizeof(l_uid_t) - sizeof(int)]; union l_sigval _sigval; l_uint _sys_private; } _timer; struct { l_pid_t _pid; /* sender's pid */ l_uid_t _uid; /* sender's uid */ union l_sigval _sigval; } _rt; struct { l_pid_t _pid; /* which child */ l_uid_t _uid; /* sender's uid */ l_int _status; /* exit code */ l_clock_t _utime; l_clock_t _stime; } _sigchld; struct { l_uintptr_t _addr; /* Faulting insn/memory ref. */ } _sigfault; struct { l_long _band; /* POLL_IN,POLL_OUT,POLL_MSG */ l_int _fd; } _sigpoll; } _sifields; } l_siginfo_t; #define lsi_pid _sifields._kill._pid #define lsi_uid _sifields._kill._uid #define lsi_tid _sifields._timer._tid #define lsi_overrun _sifields._timer._overrun #define lsi_sys_private _sifields._timer._sys_private #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.sival_int #define lsi_ptr _sifields._rt._sigval.sival_ptr #define lsi_addr _sifields._sigfault._addr #define lsi_band _sifields._sigpoll._band #define lsi_fd _sifields._sigpoll._fd /* * We make the stack look like Linux expects it when calling a signal * handler, but use the BSD way of calling the handler and sigreturn(). * This means that we need to pass the pointer to the handler too. * It is appended to the frame to not interfere with the rest of it. */ struct l_rt_sigframe { struct l_ucontext sf_sc; struct l_siginfo sf_si; l_handler_t sf_handler; }; /* * mount flags */ #define LINUX_MS_RDONLY 0x0001 #define LINUX_MS_NOSUID 0x0002 #define LINUX_MS_NODEV 0x0004 #define LINUX_MS_NOEXEC 0x0008 #define LINUX_MS_REMOUNT 0x0020 /* * SystemV IPC defines */ #define LINUX_IPC_RMID 0 #define LINUX_IPC_SET 1 #define LINUX_IPC_STAT 2 #define LINUX_IPC_INFO 3 #define LINUX_SHM_LOCK 11 #define LINUX_SHM_UNLOCK 12 #define LINUX_SHM_STAT 13 #define LINUX_SHM_INFO 14 #define LINUX_SHM_RDONLY 0x1000 #define LINUX_SHM_RND 0x2000 #define LINUX_SHM_REMAP 0x4000 /* semctl commands */ #define LINUX_GETPID 11 #define LINUX_GETVAL 12 #define LINUX_GETALL 13 #define LINUX_GETNCNT 14 #define LINUX_GETZCNT 15 #define LINUX_SETVAL 16 #define LINUX_SETALL 17 #define LINUX_SEM_STAT 18 #define LINUX_SEM_INFO 19 union l_semun { l_int val; l_uintptr_t buf; l_uintptr_t array; l_uintptr_t __buf; l_uintptr_t __pad; }; struct l_ifmap { l_ulong mem_start; l_ulong mem_end; l_ushort base_addr; u_char irq; u_char dma; u_char port; } __packed; struct l_ifreq { union { char ifrn_name[LINUX_IFNAMSIZ]; } ifr_ifrn; union { struct l_sockaddr ifru_addr; struct l_sockaddr ifru_dstaddr; struct l_sockaddr ifru_broadaddr; struct l_sockaddr ifru_netmask; struct l_sockaddr ifru_hwaddr; l_short ifru_flags[1]; l_int ifru_ivalue; l_int ifru_mtu; struct l_ifmap ifru_map; char ifru_slave[LINUX_IFNAMSIZ]; l_uintptr_t ifru_data; } ifr_ifru; } __packed; #define ifr_name ifr_ifrn.ifrn_name /* Interface name */ #define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ #define ifr_ifindex ifr_ifru.ifru_ivalue /* Interface index */ struct l_ifconf { int ifc_len; union { l_uintptr_t ifcu_buf; l_uintptr_t ifcu_req; } ifc_ifcu; }; #define ifc_buf ifc_ifcu.ifcu_buf #define ifc_req ifc_ifcu.ifcu_req /* * poll() */ #define LINUX_POLLIN 0x0001 #define LINUX_POLLPRI 0x0002 #define LINUX_POLLOUT 0x0004 #define LINUX_POLLERR 0x0008 #define LINUX_POLLHUP 0x0010 #define LINUX_POLLNVAL 0x0020 #define LINUX_POLLRDNORM 0x0040 #define LINUX_POLLRDBAND 0x0080 #define LINUX_POLLWRNORM 0x0100 #define LINUX_POLLWRBAND 0x0200 #define LINUX_POLLMSG 0x0400 struct l_pollfd { l_int fd; l_short events; l_short revents; }; #define LINUX_ARCH_SET_GS 0x1001 #define LINUX_ARCH_SET_FS 0x1002 #define LINUX_ARCH_GET_FS 0x1003 #define LINUX_ARCH_GET_GS 0x1004 #define linux_copyout_rusage(r, u) copyout(r, u, sizeof(*r)) /* robust futexes */ struct linux_robust_list { l_uintptr_t next; }; struct linux_robust_list_head { struct linux_robust_list list; l_long futex_offset; l_uintptr_t pending_list; }; #endif /* !_AMD64_LINUX_H_ */ Index: stable/12/sys/amd64/linux32/linux.h =================================================================== --- stable/12/sys/amd64/linux32/linux.h (revision 360445) +++ stable/12/sys/amd64/linux32/linux.h (revision 360446) @@ -1,648 +1,642 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2004 Tim J. Robbins * Copyright (c) 2001 Doug Rabson * Copyright (c) 1994-1996 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _AMD64_LINUX_H_ #define _AMD64_LINUX_H_ +#include + #include #include #define LINUX_LEGACY_SYSCALLS /* * debugging support */ extern u_char linux_debug_map[]; #define ldebug(name) isclr(linux_debug_map, LINUX32_SYS_linux_ ## name) #define ARGS(nm, fmt) "linux(%ld/%ld): "#nm"("fmt")\n", \ (long)td->td_proc->p_pid, (long)td->td_tid #define LMSG(fmt) "linux(%ld/%ld): "fmt"\n", \ (long)td->td_proc->p_pid, (long)td->td_tid #define LINUX_DTRACE linuxulator32 #define LINUX32_MAXUSER ((1ul << 32) - PAGE_SIZE) #define LINUX32_SHAREDPAGE (LINUX32_MAXUSER - PAGE_SIZE) #define LINUX32_USRSTACK LINUX32_SHAREDPAGE /* XXX 16 = sizeof(linux32_ps_strings) */ #define LINUX32_PS_STRINGS (LINUX32_USRSTACK - 16) #define LINUX32_MAXDSIZ (512 * 1024 * 1024) /* 512MB */ #define LINUX32_MAXSSIZ (64 * 1024 * 1024) /* 64MB */ #define LINUX32_MAXVMEM 0 /* Unlimited */ - -#define PTRIN(v) (void *)(uintptr_t)(v) -#define PTROUT(v) (l_uintptr_t)(uintptr_t)(v) - -#define CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0) -#define CP2(src,dst,sfld,dfld) do { (dst).dfld = (src).sfld; } while (0) -#define PTRIN_CP(src,dst,fld) \ - do { (dst).fld = PTRIN((src).fld); } while (0) /* * Provide a separate set of types for the Linux types. */ typedef int l_int; typedef int32_t l_long; typedef int64_t l_longlong; typedef short l_short; typedef unsigned int l_uint; typedef uint32_t l_ulong; typedef uint64_t l_ulonglong; typedef unsigned short l_ushort; typedef l_ulong l_uintptr_t; typedef l_long l_clock_t; typedef l_int l_daddr_t; typedef l_ushort l_dev_t; typedef l_uint l_gid_t; typedef l_ushort l_gid16_t; typedef l_ulong l_ino_t; typedef l_int l_key_t; typedef l_longlong l_loff_t; typedef l_ushort l_mode_t; typedef l_long l_off_t; typedef l_int l_pid_t; typedef l_uint l_size_t; typedef l_long l_suseconds_t; typedef l_long l_time_t; typedef l_uint l_uid_t; typedef l_ushort l_uid16_t; typedef l_int l_timer_t; typedef l_int l_mqd_t; typedef l_ulong l_fd_mask; typedef struct { l_int val[2]; } __packed l_fsid_t; typedef struct { l_time_t tv_sec; l_suseconds_t tv_usec; } l_timeval; #define l_fd_set fd_set /* * Miscellaneous */ #define LINUX_AT_COUNT 20 /* Count of used aux entry types. * Keep this synchronized with * linux_fixup_elf() code. */ struct l___sysctl_args { l_uintptr_t name; l_int nlen; l_uintptr_t oldval; l_uintptr_t oldlenp; l_uintptr_t newval; l_size_t newlen; l_ulong __spare[4]; } __packed; /* Resource limits */ #define LINUX_RLIMIT_CPU 0 #define LINUX_RLIMIT_FSIZE 1 #define LINUX_RLIMIT_DATA 2 #define LINUX_RLIMIT_STACK 3 #define LINUX_RLIMIT_CORE 4 #define LINUX_RLIMIT_RSS 5 #define LINUX_RLIMIT_NPROC 6 #define LINUX_RLIMIT_NOFILE 7 #define LINUX_RLIMIT_MEMLOCK 8 #define LINUX_RLIMIT_AS 9 /* Address space limit */ #define LINUX_RLIM_NLIMITS 10 struct l_rlimit { l_ulong rlim_cur; l_ulong rlim_max; } __packed; struct l_rusage { l_timeval ru_utime; l_timeval ru_stime; l_long ru_maxrss; l_long ru_ixrss; l_long ru_idrss; l_long ru_isrss; l_long ru_minflt; l_long ru_majflt; l_long ru_nswap; l_long ru_inblock; l_long ru_oublock; l_long ru_msgsnd; l_long ru_msgrcv; l_long ru_nsignals; l_long ru_nvcsw; l_long ru_nivcsw; } __packed; struct l_mmap_argv { l_uintptr_t addr; l_size_t len; l_int prot; l_int flags; l_int fd; l_ulong pgoff; }; /* * stat family of syscalls */ struct l_timespec { l_time_t tv_sec; l_long tv_nsec; } __packed; struct l_newstat { l_ushort st_dev; l_ushort __pad1; l_ulong st_ino; l_ushort st_mode; l_ushort st_nlink; l_ushort st_uid; l_ushort st_gid; l_ushort st_rdev; l_ushort __pad2; l_ulong st_size; l_ulong st_blksize; l_ulong st_blocks; struct l_timespec st_atim; struct l_timespec st_mtim; struct l_timespec st_ctim; l_ulong __unused4; l_ulong __unused5; } __packed; struct l_stat { l_ushort st_dev; l_ulong st_ino; l_ushort st_mode; l_ushort st_nlink; l_ushort st_uid; l_ushort st_gid; l_ushort st_rdev; l_long st_size; struct l_timespec st_atim; struct l_timespec st_mtim; struct l_timespec st_ctim; l_long st_blksize; l_long st_blocks; l_ulong st_flags; l_ulong st_gen; }; struct l_stat64 { l_ushort st_dev; u_char __pad0[10]; l_ulong __st_ino; l_uint st_mode; l_uint st_nlink; l_ulong st_uid; l_ulong st_gid; l_ushort st_rdev; u_char __pad3[10]; l_longlong st_size; l_ulong st_blksize; l_ulong st_blocks; l_ulong __pad4; struct l_timespec st_atim; struct l_timespec st_mtim; struct l_timespec st_ctim; l_ulonglong st_ino; } __packed; struct l_statfs64 { l_int f_type; l_int f_bsize; uint64_t f_blocks; uint64_t f_bfree; uint64_t f_bavail; uint64_t f_files; uint64_t f_ffree; l_fsid_t f_fsid; l_int f_namelen; l_int f_frsize; l_int f_flags; l_int f_spare[4]; } __packed; /* sigaction flags */ #define LINUX_SA_NOCLDSTOP 0x00000001 #define LINUX_SA_NOCLDWAIT 0x00000002 #define LINUX_SA_SIGINFO 0x00000004 #define LINUX_SA_RESTORER 0x04000000 #define LINUX_SA_ONSTACK 0x08000000 #define LINUX_SA_RESTART 0x10000000 #define LINUX_SA_INTERRUPT 0x20000000 #define LINUX_SA_NOMASK 0x40000000 #define LINUX_SA_ONESHOT 0x80000000 /* sigprocmask actions */ #define LINUX_SIG_BLOCK 0 #define LINUX_SIG_UNBLOCK 1 #define LINUX_SIG_SETMASK 2 /* sigaltstack */ #define LINUX_MINSIGSTKSZ 2048 typedef l_uintptr_t l_handler_t; typedef l_ulong l_osigset_t; typedef struct { l_handler_t lsa_handler; l_osigset_t lsa_mask; l_ulong lsa_flags; l_uintptr_t lsa_restorer; } __packed l_osigaction_t; typedef struct { l_handler_t lsa_handler; l_ulong lsa_flags; l_uintptr_t lsa_restorer; l_sigset_t lsa_mask; } __packed l_sigaction_t; typedef struct { l_uintptr_t ss_sp; l_int ss_flags; l_size_t ss_size; } __packed l_stack_t; /* The Linux sigcontext, pretty much a standard 386 trapframe. */ struct l_sigcontext { l_uint sc_gs; l_uint sc_fs; l_uint sc_es; l_uint sc_ds; l_uint sc_edi; l_uint sc_esi; l_uint sc_ebp; l_uint sc_esp; l_uint sc_ebx; l_uint sc_edx; l_uint sc_ecx; l_uint sc_eax; l_uint sc_trapno; l_uint sc_err; l_uint sc_eip; l_uint sc_cs; l_uint sc_eflags; l_uint sc_esp_at_signal; l_uint sc_ss; l_uint sc_387; l_uint sc_mask; l_uint sc_cr2; } __packed; struct l_ucontext { l_ulong uc_flags; l_uintptr_t uc_link; l_stack_t uc_stack; struct l_sigcontext uc_mcontext; l_sigset_t uc_sigmask; } __packed; #define LINUX_SI_MAX_SIZE 128 #define LINUX_SI_PAD_SIZE ((LINUX_SI_MAX_SIZE/sizeof(l_int)) - 3) typedef union l_sigval { l_int sival_int; l_uintptr_t sival_ptr; } l_sigval_t; typedef struct l_siginfo { l_int lsi_signo; l_int lsi_errno; l_int lsi_code; union { l_int _pad[LINUX_SI_PAD_SIZE]; struct { l_pid_t _pid; l_uid_t _uid; } __packed _kill; struct { l_timer_t _tid; l_int _overrun; char _pad[sizeof(l_uid_t) - sizeof(l_int)]; l_sigval_t _sigval; l_int _sys_private; } __packed _timer; struct { l_pid_t _pid; /* sender's pid */ l_uid_t _uid; /* sender's uid */ l_sigval_t _sigval; } __packed _rt; struct { l_pid_t _pid; /* which child */ l_uid_t _uid; /* sender's uid */ l_int _status; /* exit code */ l_clock_t _utime; l_clock_t _stime; } __packed _sigchld; struct { l_uintptr_t _addr; /* Faulting insn/memory ref. */ } __packed _sigfault; struct { l_long _band; /* POLL_IN,POLL_OUT,POLL_MSG */ l_int _fd; } __packed _sigpoll; } _sifields; } __packed l_siginfo_t; #define lsi_pid _sifields._kill._pid #define lsi_uid _sifields._kill._uid #define lsi_tid _sifields._timer._tid #define lsi_overrun _sifields._timer._overrun #define lsi_sys_private _sifields._timer._sys_private #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.sival_int #define lsi_ptr _sifields._rt._sigval.sival_ptr #define lsi_addr _sifields._sigfault._addr #define lsi_band _sifields._sigpoll._band #define lsi_fd _sifields._sigpoll._fd struct l_fpreg { u_int16_t significand[4]; u_int16_t exponent; } __packed; struct l_fpxreg { u_int16_t significand[4]; u_int16_t exponent; u_int16_t padding[3]; } __packed; struct l_xmmreg { u_int32_t element[4]; } __packed; struct l_fpstate { /* Regular FPU environment */ u_int32_t cw; u_int32_t sw; u_int32_t tag; u_int32_t ipoff; u_int32_t cssel; u_int32_t dataoff; u_int32_t datasel; struct l_fpreg _st[8]; u_int16_t status; u_int16_t magic; /* 0xffff = regular FPU data */ /* FXSR FPU environment */ u_int32_t _fxsr_env[6]; /* env is ignored. */ u_int32_t mxcsr; u_int32_t reserved; struct l_fpxreg _fxsr_st[8]; /* reg data is ignored. */ struct l_xmmreg _xmm[8]; u_int32_t padding[56]; } __packed; /* * We make the stack look like Linux expects it when calling a signal * handler, but use the BSD way of calling the handler and sigreturn(). * This means that we need to pass the pointer to the handler too. * It is appended to the frame to not interfere with the rest of it. */ struct l_sigframe { l_int sf_sig; struct l_sigcontext sf_sc; struct l_fpstate sf_fpstate; l_uint sf_extramask[1]; l_handler_t sf_handler; } __packed; struct l_rt_sigframe { l_int sf_sig; l_uintptr_t sf_siginfo; l_uintptr_t sf_ucontext; l_siginfo_t sf_si; struct l_ucontext sf_sc; l_handler_t sf_handler; } __packed; /* * arch specific open/fcntl flags */ #define LINUX_F_GETLK64 12 #define LINUX_F_SETLK64 13 #define LINUX_F_SETLKW64 14 union l_semun { l_int val; l_uintptr_t buf; l_uintptr_t array; l_uintptr_t __buf; l_uintptr_t __pad; } __packed; struct l_ifmap { l_ulong mem_start; l_ulong mem_end; l_ushort base_addr; u_char irq; u_char dma; u_char port; } __packed; struct l_ifreq { union { char ifrn_name[LINUX_IFNAMSIZ]; } ifr_ifrn; union { struct l_sockaddr ifru_addr; struct l_sockaddr ifru_dstaddr; struct l_sockaddr ifru_broadaddr; struct l_sockaddr ifru_netmask; struct l_sockaddr ifru_hwaddr; l_short ifru_flags[1]; l_int ifru_ivalue; l_int ifru_mtu; struct l_ifmap ifru_map; char ifru_slave[LINUX_IFNAMSIZ]; l_uintptr_t ifru_data; } ifr_ifru; } __packed; #define ifr_name ifr_ifrn.ifrn_name /* Interface name */ #define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ #define ifr_ifindex ifr_ifru.ifru_ivalue /* Interface index */ struct l_ifconf { int ifc_len; union { l_uintptr_t ifcu_buf; l_uintptr_t ifcu_req; } ifc_ifcu; } __packed; #define ifc_buf ifc_ifcu.ifcu_buf #define ifc_req ifc_ifcu.ifcu_req /* * poll() */ #define LINUX_POLLIN 0x0001 #define LINUX_POLLPRI 0x0002 #define LINUX_POLLOUT 0x0004 #define LINUX_POLLERR 0x0008 #define LINUX_POLLHUP 0x0010 #define LINUX_POLLNVAL 0x0020 #define LINUX_POLLRDNORM 0x0040 #define LINUX_POLLRDBAND 0x0080 #define LINUX_POLLWRNORM 0x0100 #define LINUX_POLLWRBAND 0x0200 #define LINUX_POLLMSG 0x0400 struct l_pollfd { l_int fd; l_short events; l_short revents; } __packed; struct l_user_desc { l_uint entry_number; l_uint base_addr; l_uint limit; l_uint seg_32bit:1; l_uint contents:2; l_uint read_exec_only:1; l_uint limit_in_pages:1; l_uint seg_not_present:1; l_uint useable:1; }; #define LINUX_LOWERWORD 0x0000ffff /* * Macros which does the same thing as those in Linux include/asm-um/ldt-i386.h. * These convert Linux user space descriptor to machine one. */ #define LINUX_LDT_entry_a(info) \ ((((info)->base_addr & LINUX_LOWERWORD) << 16) | \ ((info)->limit & LINUX_LOWERWORD)) #define LINUX_ENTRY_B_READ_EXEC_ONLY 9 #define LINUX_ENTRY_B_CONTENTS 10 #define LINUX_ENTRY_B_SEG_NOT_PRESENT 15 #define LINUX_ENTRY_B_BASE_ADDR 16 #define LINUX_ENTRY_B_USEABLE 20 #define LINUX_ENTRY_B_SEG32BIT 22 #define LINUX_ENTRY_B_LIMIT 23 #define LINUX_LDT_entry_b(info) \ (((info)->base_addr & 0xff000000) | \ ((info)->limit & 0xf0000) | \ ((info)->contents << LINUX_ENTRY_B_CONTENTS) | \ (((info)->seg_not_present == 0) << LINUX_ENTRY_B_SEG_NOT_PRESENT) | \ (((info)->base_addr & 0x00ff0000) >> LINUX_ENTRY_B_BASE_ADDR) | \ (((info)->read_exec_only == 0) << LINUX_ENTRY_B_READ_EXEC_ONLY) | \ ((info)->seg_32bit << LINUX_ENTRY_B_SEG32BIT) | \ ((info)->useable << LINUX_ENTRY_B_USEABLE) | \ ((info)->limit_in_pages << LINUX_ENTRY_B_LIMIT) | 0x7000) #define LINUX_LDT_empty(info) \ ((info)->base_addr == 0 && \ (info)->limit == 0 && \ (info)->contents == 0 && \ (info)->seg_not_present == 1 && \ (info)->read_exec_only == 1 && \ (info)->seg_32bit == 0 && \ (info)->limit_in_pages == 0 && \ (info)->useable == 0) /* * Macros for converting segments. * They do the same as those in arch/i386/kernel/process.c in Linux. */ #define LINUX_GET_BASE(desc) \ ((((desc)->a >> 16) & LINUX_LOWERWORD) | \ (((desc)->b << 16) & 0x00ff0000) | \ ((desc)->b & 0xff000000)) #define LINUX_GET_LIMIT(desc) \ (((desc)->a & LINUX_LOWERWORD) | \ ((desc)->b & 0xf0000)) #define LINUX_GET_32BIT(desc) \ (((desc)->b >> LINUX_ENTRY_B_SEG32BIT) & 1) #define LINUX_GET_CONTENTS(desc) \ (((desc)->b >> LINUX_ENTRY_B_CONTENTS) & 3) #define LINUX_GET_WRITABLE(desc) \ (((desc)->b >> LINUX_ENTRY_B_READ_EXEC_ONLY) & 1) #define LINUX_GET_LIMIT_PAGES(desc) \ (((desc)->b >> LINUX_ENTRY_B_LIMIT) & 1) #define LINUX_GET_PRESENT(desc) \ (((desc)->b >> LINUX_ENTRY_B_SEG_NOT_PRESENT) & 1) #define LINUX_GET_USEABLE(desc) \ (((desc)->b >> LINUX_ENTRY_B_USEABLE) & 1) struct iovec; struct uio; struct l_iovec32 { uint32_t iov_base; l_size_t iov_len; }; int linux32_copyiniov(struct l_iovec32 *iovp32, l_ulong iovcnt, struct iovec **iovp, int error); int linux32_copyinuio(struct l_iovec32 *iovp, l_ulong iovcnt, struct uio **uiop); int linux_copyout_rusage(struct rusage *ru, void *uaddr); /* robust futexes */ struct linux_robust_list { l_uintptr_t next; }; struct linux_robust_list_head { struct linux_robust_list list; l_long futex_offset; l_uintptr_t pending_list; }; #endif /* !_AMD64_LINUX_H_ */ Index: stable/12/sys/arm64/linux/linux.h =================================================================== --- stable/12/sys/arm64/linux/linux.h (revision 360445) +++ stable/12/sys/arm64/linux/linux.h (revision 360446) @@ -1,313 +1,307 @@ /*- * Copyright (c) 1994-1996 Søren Schmidt * Copyright (c) 2013 Dmitry Chagin * Copyright (c) 2018 Turing Robotic Industries Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $FreeBSD$ */ #ifndef _ARM64_LINUX_H_ #define _ARM64_LINUX_H_ +#include + #include #include /* Debugging support */ #define DEBUG extern u_char linux_debug_map[]; #define ldebug(name) isclr(linux_debug_map, LINUX_SYS_linux_ ## name) #define ARGS(nm, fmt) "linux(%ld/%ld): "#nm"("fmt")\n", \ (long)td->td_proc->p_pid, (long)td->td_tid #define LMSG(fmt) "linux(%ld/%ld): "fmt"\n", \ (long)td->td_proc->p_pid, (long)td->td_tid #define LINUX_DTRACE linuxulator - -#define PTRIN(v) (void *)(v) -#define PTROUT(v) (uintptr_t)(v) - -#define CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0) -#define CP2(src,dst,sfld,dfld) do { (dst).dfld = (src).sfld; } while (0) -#define PTRIN_CP(src,dst,fld) \ - do { (dst).fld = PTRIN((src).fld); } while (0) /* Provide a separate set of types for the Linux types */ typedef int32_t l_int; typedef int64_t l_long; typedef int16_t l_short; typedef uint32_t l_uint; typedef uint64_t l_ulong; typedef uint16_t l_ushort; typedef l_ulong l_uintptr_t; typedef l_long l_clock_t; typedef l_int l_daddr_t; typedef l_ulong l_dev_t; typedef l_uint l_gid_t; typedef l_ushort l_gid16_t; /* XXX */ typedef l_uint l_uid_t; typedef l_ushort l_uid16_t; /* XXX */ typedef l_ulong l_ino_t; typedef l_int l_key_t; typedef l_long l_loff_t; typedef l_uint l_mode_t; typedef l_long l_off_t; typedef l_int l_pid_t; typedef l_ulong l_size_t; typedef l_long l_suseconds_t; typedef l_long l_time_t; typedef l_int l_timer_t; /* XXX */ typedef l_ulong l_fd_mask; typedef struct { l_int val[2]; } l_fsid_t; typedef struct { l_time_t tv_sec; l_suseconds_t tv_usec; } l_timeval; #define l_fd_set fd_set /* Miscellaneous */ #define LINUX_AT_COUNT 20 struct l___sysctl_args { l_uintptr_t name; l_int nlen; l_uintptr_t oldval; l_uintptr_t oldlenp; l_uintptr_t newval; l_uintptr_t newlen; l_ulong __spare[4]; }; /* Resource limits */ #define LINUX_RLIMIT_CPU 0 #define LINUX_RLIMIT_FSIZE 1 #define LINUX_RLIMIT_DATA 2 #define LINUX_RLIMIT_STACK 3 #define LINUX_RLIMIT_CORE 4 #define LINUX_RLIMIT_RSS 5 #define LINUX_RLIMIT_NPROC 6 #define LINUX_RLIMIT_NOFILE 7 #define LINUX_RLIMIT_MEMLOCK 8 #define LINUX_RLIMIT_AS 9 /* Address space limit */ #define LINUX_RLIM_NLIMITS 10 struct l_rlimit { l_ulong rlim_cur; l_ulong rlim_max; }; /* stat family of syscalls */ struct l_timespec { l_time_t tv_sec; l_long tv_nsec; }; struct l_newstat { l_dev_t st_dev; l_ino_t st_ino; l_uint st_mode; l_uint st_nlink; l_uid_t st_uid; l_gid_t st_gid; l_dev_t st_rdev; l_ulong __st_pad1; l_off_t st_size; l_int st_blksize; l_int __st_pad2; l_long st_blocks; struct l_timespec st_atim; struct l_timespec st_mtim; struct l_timespec st_ctim; l_uint __unused1; l_uint __unused2; }; /* sigaction flags */ #define LINUX_SA_NOCLDSTOP 0x00000001 #define LINUX_SA_NOCLDWAIT 0x00000002 #define LINUX_SA_SIGINFO 0x00000004 #define LINUX_SA_RESTORER 0x04000000 #define LINUX_SA_ONSTACK 0x08000000 #define LINUX_SA_RESTART 0x10000000 #define LINUX_SA_INTERRUPT 0x20000000 /* XXX */ #define LINUX_SA_NOMASK 0x40000000 /* SA_NODEFER */ #define LINUX_SA_ONESHOT 0x80000000 /* SA_RESETHAND */ /* sigprocmask actions */ #define LINUX_SIG_BLOCK 0 #define LINUX_SIG_UNBLOCK 1 #define LINUX_SIG_SETMASK 2 /* sigaltstack */ #define LINUX_MINSIGSTKSZ 2048 /* XXX */ typedef void (*l_handler_t)(l_int); typedef struct { l_handler_t lsa_handler; l_sigset_t lsa_mask; l_ulong lsa_flags; l_uintptr_t lsa_restorer; } l_sigaction_t; /* XXX */ typedef struct { l_uintptr_t ss_sp; l_int ss_flags; l_size_t ss_size; } l_stack_t; #define LINUX_SI_PREAMBLE_SIZE (4 * sizeof(int)) #define LINUX_SI_MAX_SIZE 128 #define LINUX_SI_PAD_SIZE ((LINUX_SI_MAX_SIZE - \ LINUX_SI_PREAMBLE_SIZE) / sizeof(l_int)) typedef union l_sigval { l_int sival_int; l_uintptr_t sival_ptr; } l_sigval_t; typedef struct l_siginfo { l_int lsi_signo; l_int lsi_errno; l_int lsi_code; union { l_int _pad[LINUX_SI_PAD_SIZE]; struct { l_pid_t _pid; l_uid_t _uid; } _kill; struct { l_timer_t _tid; l_int _overrun; char _pad[sizeof(l_uid_t) - sizeof(int)]; union l_sigval _sigval; l_uint _sys_private; } _timer; struct { l_pid_t _pid; /* sender's pid */ l_uid_t _uid; /* sender's uid */ union l_sigval _sigval; } _rt; struct { l_pid_t _pid; /* which child */ l_uid_t _uid; /* sender's uid */ l_int _status; /* exit code */ l_clock_t _utime; l_clock_t _stime; } _sigchld; struct { l_uintptr_t _addr; /* Faulting insn/memory ref. */ } _sigfault; struct { l_long _band; /* POLL_IN,POLL_OUT,POLL_MSG */ l_int _fd; } _sigpoll; } _sifields; } l_siginfo_t; #define lsi_pid _sifields._kill._pid #define lsi_uid _sifields._kill._uid #define lsi_tid _sifields._timer._tid #define lsi_overrun _sifields._timer._overrun #define lsi_sys_private _sifields._timer._sys_private #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.sival_int #define lsi_ptr _sifields._rt._sigval.sival_ptr #define lsi_addr _sifields._sigfault._addr #define lsi_band _sifields._sigpoll._band #define lsi_fd _sifields._sigpoll._fd union l_semun { l_int val; l_uintptr_t buf; l_uintptr_t array; l_uintptr_t __buf; l_uintptr_t __pad; }; struct l_ifmap { l_ulong mem_start; l_ulong mem_end; l_ushort base_addr; u_char irq; u_char dma; u_char port; } __packed; struct l_ifreq { union { char ifrn_name[LINUX_IFNAMSIZ]; } ifr_ifrn; union { struct l_sockaddr ifru_addr; struct l_sockaddr ifru_dstaddr; struct l_sockaddr ifru_broadaddr; struct l_sockaddr ifru_netmask; struct l_sockaddr ifru_hwaddr; l_short ifru_flags[1]; l_int ifru_ivalue; l_int ifru_mtu; struct l_ifmap ifru_map; char ifru_slave[LINUX_IFNAMSIZ]; l_uintptr_t ifru_data; } ifr_ifru; } __packed; #define ifr_name ifr_ifrn.ifrn_name /* Interface name */ #define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ #define ifr_ifindex ifr_ifru.ifru_ivalue /* Interface index */ #define linux_copyout_rusage(r, u) copyout(r, u, sizeof(*r)) /* robust futexes */ struct linux_robust_list { l_uintptr_t next; }; struct linux_robust_list_head { struct linux_robust_list list; l_long futex_offset; l_uintptr_t pending_list; }; #endif /* _ARM64_LINUX_H_ */ Index: stable/12/sys/compat/freebsd32/freebsd32.h =================================================================== --- stable/12/sys/compat/freebsd32/freebsd32.h (revision 360445) +++ stable/12/sys/compat/freebsd32/freebsd32.h (revision 360446) @@ -1,415 +1,391 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _COMPAT_FREEBSD32_FREEBSD32_H_ #define _COMPAT_FREEBSD32_FREEBSD32_H_ +#include #include #include #include -#define PTRIN(v) (void *)(uintptr_t) (v) -#define PTROUT(v) (u_int32_t)(uintptr_t) (v) - -#define CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0) -#define PTRIN_CP(src,dst,fld) \ - do { (dst).fld = PTRIN((src).fld); } while (0) -#define PTROUT_CP(src,dst,fld) \ - do { (dst).fld = PTROUT((src).fld); } while (0) - /* * i386 is the only arch with a 32-bit time_t */ #ifdef __amd64__ typedef int32_t time32_t; #else typedef int64_t time32_t; #endif struct timeval32 { time32_t tv_sec; int32_t tv_usec; }; -#define TV_CP(src,dst,fld) do { \ - CP((src).fld,(dst).fld,tv_sec); \ - CP((src).fld,(dst).fld,tv_usec); \ -} while (0) struct timespec32 { time32_t tv_sec; int32_t tv_nsec; }; -#define TS_CP(src,dst,fld) do { \ - CP((src).fld,(dst).fld,tv_sec); \ - CP((src).fld,(dst).fld,tv_nsec); \ -} while (0) struct itimerspec32 { struct timespec32 it_interval; struct timespec32 it_value; }; -#define ITS_CP(src, dst) do { \ - TS_CP((src), (dst), it_interval); \ - TS_CP((src), (dst), it_value); \ -} while (0) struct bintime32 { time32_t sec; uint32_t frac[2]; }; -#define BT_CP(src, dst, fld) do { \ - CP((src).fld, (dst).fld, sec); \ - *(uint64_t *)&(dst).fld.frac[0] = (src).fld.frac; \ -} while (0) struct rusage32 { struct timeval32 ru_utime; struct timeval32 ru_stime; int32_t ru_maxrss; int32_t ru_ixrss; int32_t ru_idrss; int32_t ru_isrss; int32_t ru_minflt; int32_t ru_majflt; int32_t ru_nswap; int32_t ru_inblock; int32_t ru_oublock; int32_t ru_msgsnd; int32_t ru_msgrcv; int32_t ru_nsignals; int32_t ru_nvcsw; int32_t ru_nivcsw; }; struct wrusage32 { struct rusage32 wru_self; struct rusage32 wru_children; }; struct itimerval32 { struct timeval32 it_interval; struct timeval32 it_value; }; #define FREEBSD4_MFSNAMELEN 16 #define FREEBSD4_MNAMELEN (88 - 2 * sizeof(int32_t)) /* 4.x version */ struct statfs32 { int32_t f_spare2; int32_t f_bsize; int32_t f_iosize; int32_t f_blocks; int32_t f_bfree; int32_t f_bavail; int32_t f_files; int32_t f_ffree; fsid_t f_fsid; uid_t f_owner; int32_t f_type; int32_t f_flags; int32_t f_syncwrites; int32_t f_asyncwrites; char f_fstypename[FREEBSD4_MFSNAMELEN]; char f_mntonname[FREEBSD4_MNAMELEN]; int32_t f_syncreads; int32_t f_asyncreads; int16_t f_spares1; char f_mntfromname[FREEBSD4_MNAMELEN]; int16_t f_spares2 __packed; int32_t f_spare[2]; }; struct iovec32 { u_int32_t iov_base; int iov_len; }; struct msghdr32 { u_int32_t msg_name; socklen_t msg_namelen; u_int32_t msg_iov; int msg_iovlen; u_int32_t msg_control; socklen_t msg_controllen; int msg_flags; }; #if defined(__amd64__) #define __STAT32_TIME_T_EXT 1 #endif struct stat32 { dev_t st_dev; ino_t st_ino; nlink_t st_nlink; mode_t st_mode; u_int16_t st_padding0; uid_t st_uid; gid_t st_gid; u_int32_t st_padding1; dev_t st_rdev; #ifdef __STAT32_TIME_T_EXT __int32_t st_atim_ext; #endif struct timespec32 st_atim; #ifdef __STAT32_TIME_T_EXT __int32_t st_mtim_ext; #endif struct timespec32 st_mtim; #ifdef __STAT32_TIME_T_EXT __int32_t st_ctim_ext; #endif struct timespec32 st_ctim; #ifdef __STAT32_TIME_T_EXT __int32_t st_btim_ext; #endif struct timespec32 st_birthtim; off_t st_size; int64_t st_blocks; u_int32_t st_blksize; u_int32_t st_flags; u_int64_t st_gen; u_int64_t st_spare[10]; }; struct freebsd11_stat32 { u_int32_t st_dev; u_int32_t st_ino; mode_t st_mode; u_int16_t st_nlink; uid_t st_uid; gid_t st_gid; u_int32_t st_rdev; struct timespec32 st_atim; struct timespec32 st_mtim; struct timespec32 st_ctim; off_t st_size; int64_t st_blocks; u_int32_t st_blksize; u_int32_t st_flags; u_int32_t st_gen; int32_t st_lspare; struct timespec32 st_birthtim; unsigned int :(8 / 2) * (16 - (int)sizeof(struct timespec32)); unsigned int :(8 / 2) * (16 - (int)sizeof(struct timespec32)); }; struct ostat32 { __uint16_t st_dev; __uint32_t st_ino; mode_t st_mode; __uint16_t st_nlink; __uint16_t st_uid; __uint16_t st_gid; __uint16_t st_rdev; __int32_t st_size; struct timespec32 st_atim; struct timespec32 st_mtim; struct timespec32 st_ctim; __int32_t st_blksize; __int32_t st_blocks; u_int32_t st_flags; __uint32_t st_gen; }; struct jail32_v0 { u_int32_t version; uint32_t path; uint32_t hostname; u_int32_t ip_number; }; struct jail32 { uint32_t version; uint32_t path; uint32_t hostname; uint32_t jailname; uint32_t ip4s; uint32_t ip6s; uint32_t ip4; uint32_t ip6; }; struct sigaction32 { u_int32_t sa_u; int sa_flags; sigset_t sa_mask; }; struct thr_param32 { uint32_t start_func; uint32_t arg; uint32_t stack_base; uint32_t stack_size; uint32_t tls_base; uint32_t tls_size; uint32_t child_tid; uint32_t parent_tid; int32_t flags; uint32_t rtp; uint32_t spare[3]; }; struct i386_ldt_args32 { uint32_t start; uint32_t descs; uint32_t num; }; struct mq_attr32 { int mq_flags; int mq_maxmsg; int mq_msgsize; int mq_curmsgs; int __reserved[4]; }; struct kinfo_proc32 { int ki_structsize; int ki_layout; uint32_t ki_args; uint32_t ki_paddr; uint32_t ki_addr; uint32_t ki_tracep; uint32_t ki_textvp; uint32_t ki_fd; uint32_t ki_vmspace; uint32_t ki_wchan; pid_t ki_pid; pid_t ki_ppid; pid_t ki_pgid; pid_t ki_tpgid; pid_t ki_sid; pid_t ki_tsid; short ki_jobc; short ki_spare_short1; uint32_t ki_tdev_freebsd11; sigset_t ki_siglist; sigset_t ki_sigmask; sigset_t ki_sigignore; sigset_t ki_sigcatch; uid_t ki_uid; uid_t ki_ruid; uid_t ki_svuid; gid_t ki_rgid; gid_t ki_svgid; short ki_ngroups; short ki_spare_short2; gid_t ki_groups[KI_NGROUPS]; uint32_t ki_size; int32_t ki_rssize; int32_t ki_swrss; int32_t ki_tsize; int32_t ki_dsize; int32_t ki_ssize; u_short ki_xstat; u_short ki_acflag; fixpt_t ki_pctcpu; u_int ki_estcpu; u_int ki_slptime; u_int ki_swtime; u_int ki_cow; u_int64_t ki_runtime; struct timeval32 ki_start; struct timeval32 ki_childtime; int ki_flag; int ki_kiflag; int ki_traceflag; char ki_stat; signed char ki_nice; char ki_lock; char ki_rqindex; u_char ki_oncpu_old; u_char ki_lastcpu_old; char ki_tdname[TDNAMLEN+1]; char ki_wmesg[WMESGLEN+1]; char ki_login[LOGNAMELEN+1]; char ki_lockname[LOCKNAMELEN+1]; char ki_comm[COMMLEN+1]; char ki_emul[KI_EMULNAMELEN+1]; char ki_loginclass[LOGINCLASSLEN+1]; char ki_moretdname[MAXCOMLEN-TDNAMLEN+1]; char ki_sparestrings[46]; int ki_spareints[KI_NSPARE_INT]; uint64_t ki_tdev; int ki_oncpu; int ki_lastcpu; int ki_tracer; int ki_flag2; int ki_fibnum; u_int ki_cr_flags; int ki_jid; int ki_numthreads; lwpid_t ki_tid; struct priority ki_pri; struct rusage32 ki_rusage; struct rusage32 ki_rusage_ch; uint32_t ki_pcb; uint32_t ki_kstack; uint32_t ki_udata; uint32_t ki_tdaddr; uint32_t ki_spareptrs[KI_NSPARE_PTR]; /* spare room for growth */ int ki_sparelongs[KI_NSPARE_LONG]; int ki_sflag; int ki_tdflags; }; struct kinfo_sigtramp32 { uint32_t ksigtramp_start; uint32_t ksigtramp_end; uint32_t ksigtramp_spare[4]; }; struct kld32_file_stat_1 { int version; /* set to sizeof(struct kld_file_stat_1) */ char name[MAXPATHLEN]; int refs; int id; uint32_t address; /* load address */ uint32_t size; /* size in bytes */ }; struct kld32_file_stat { int version; /* set to sizeof(struct kld_file_stat) */ char name[MAXPATHLEN]; int refs; int id; uint32_t address; /* load address */ uint32_t size; /* size in bytes */ char pathname[MAXPATHLEN]; }; struct procctl_reaper_pids32 { u_int rp_count; u_int rp_pad0[15]; uint32_t rp_pids; }; #endif /* !_COMPAT_FREEBSD32_FREEBSD32_H_ */ Index: stable/12/sys/compat/linux/linux_ioctl.c =================================================================== --- stable/12/sys/compat/linux/linux_ioctl.c (revision 360445) +++ stable/12/sys/compat/linux/linux_ioctl.c (revision 360446) @@ -1,3818 +1,3815 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1994-1995 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "opt_compat.h" #include __FBSDID("$FreeBSD$"); #include #include #include +#ifdef COMPAT_LINUX32 +#include +#endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef COMPAT_LINUX32 #include #include #else #include #include #endif #include #include #include #include #include #include #include #include #include #include #include CTASSERT(LINUX_IFNAMSIZ == IFNAMSIZ); static linux_ioctl_function_t linux_ioctl_cdrom; static linux_ioctl_function_t linux_ioctl_vfat; static linux_ioctl_function_t linux_ioctl_console; static linux_ioctl_function_t linux_ioctl_hdio; static linux_ioctl_function_t linux_ioctl_disk; static linux_ioctl_function_t linux_ioctl_socket; static linux_ioctl_function_t linux_ioctl_sound; static linux_ioctl_function_t linux_ioctl_termio; static linux_ioctl_function_t linux_ioctl_private; static linux_ioctl_function_t linux_ioctl_drm; static linux_ioctl_function_t linux_ioctl_sg; static linux_ioctl_function_t linux_ioctl_v4l; static linux_ioctl_function_t linux_ioctl_v4l2; static linux_ioctl_function_t linux_ioctl_special; static linux_ioctl_function_t linux_ioctl_fbsd_usb; static linux_ioctl_function_t linux_ioctl_evdev; static struct linux_ioctl_handler cdrom_handler = { linux_ioctl_cdrom, LINUX_IOCTL_CDROM_MIN, LINUX_IOCTL_CDROM_MAX }; static struct linux_ioctl_handler vfat_handler = { linux_ioctl_vfat, LINUX_IOCTL_VFAT_MIN, LINUX_IOCTL_VFAT_MAX }; static struct linux_ioctl_handler console_handler = { linux_ioctl_console, LINUX_IOCTL_CONSOLE_MIN, LINUX_IOCTL_CONSOLE_MAX }; static struct linux_ioctl_handler hdio_handler = { linux_ioctl_hdio, LINUX_IOCTL_HDIO_MIN, LINUX_IOCTL_HDIO_MAX }; static struct linux_ioctl_handler disk_handler = { linux_ioctl_disk, LINUX_IOCTL_DISK_MIN, LINUX_IOCTL_DISK_MAX }; static struct linux_ioctl_handler socket_handler = { linux_ioctl_socket, LINUX_IOCTL_SOCKET_MIN, LINUX_IOCTL_SOCKET_MAX }; static struct linux_ioctl_handler sound_handler = { linux_ioctl_sound, LINUX_IOCTL_SOUND_MIN, LINUX_IOCTL_SOUND_MAX }; static struct linux_ioctl_handler termio_handler = { linux_ioctl_termio, LINUX_IOCTL_TERMIO_MIN, LINUX_IOCTL_TERMIO_MAX }; static struct linux_ioctl_handler private_handler = { linux_ioctl_private, LINUX_IOCTL_PRIVATE_MIN, LINUX_IOCTL_PRIVATE_MAX }; static struct linux_ioctl_handler drm_handler = { linux_ioctl_drm, LINUX_IOCTL_DRM_MIN, LINUX_IOCTL_DRM_MAX }; static struct linux_ioctl_handler sg_handler = { linux_ioctl_sg, LINUX_IOCTL_SG_MIN, LINUX_IOCTL_SG_MAX }; static struct linux_ioctl_handler video_handler = { linux_ioctl_v4l, LINUX_IOCTL_VIDEO_MIN, LINUX_IOCTL_VIDEO_MAX }; static struct linux_ioctl_handler video2_handler = { linux_ioctl_v4l2, LINUX_IOCTL_VIDEO2_MIN, LINUX_IOCTL_VIDEO2_MAX }; static struct linux_ioctl_handler fbsd_usb = { linux_ioctl_fbsd_usb, FBSD_LUSB_MIN, FBSD_LUSB_MAX }; static struct linux_ioctl_handler evdev_handler = { linux_ioctl_evdev, LINUX_IOCTL_EVDEV_MIN, LINUX_IOCTL_EVDEV_MAX }; DATA_SET(linux_ioctl_handler_set, cdrom_handler); DATA_SET(linux_ioctl_handler_set, vfat_handler); DATA_SET(linux_ioctl_handler_set, console_handler); DATA_SET(linux_ioctl_handler_set, hdio_handler); DATA_SET(linux_ioctl_handler_set, disk_handler); DATA_SET(linux_ioctl_handler_set, socket_handler); DATA_SET(linux_ioctl_handler_set, sound_handler); DATA_SET(linux_ioctl_handler_set, termio_handler); DATA_SET(linux_ioctl_handler_set, private_handler); DATA_SET(linux_ioctl_handler_set, drm_handler); DATA_SET(linux_ioctl_handler_set, sg_handler); DATA_SET(linux_ioctl_handler_set, video_handler); DATA_SET(linux_ioctl_handler_set, video2_handler); DATA_SET(linux_ioctl_handler_set, fbsd_usb); DATA_SET(linux_ioctl_handler_set, evdev_handler); #ifdef __i386__ static TAILQ_HEAD(, linux_ioctl_handler_element) linux_ioctl_handlers = TAILQ_HEAD_INITIALIZER(linux_ioctl_handlers); static struct sx linux_ioctl_sx; SX_SYSINIT(linux_ioctl, &linux_ioctl_sx, "Linux ioctl handlers"); #else extern TAILQ_HEAD(, linux_ioctl_handler_element) linux_ioctl_handlers; extern struct sx linux_ioctl_sx; #endif #ifdef COMPAT_LINUX32 static TAILQ_HEAD(, linux_ioctl_handler_element) linux32_ioctl_handlers = TAILQ_HEAD_INITIALIZER(linux32_ioctl_handlers); #endif /* * hdio related ioctls for VMWare support */ struct linux_hd_geometry { u_int8_t heads; u_int8_t sectors; u_int16_t cylinders; u_int32_t start; }; struct linux_hd_big_geometry { u_int8_t heads; u_int8_t sectors; u_int32_t cylinders; u_int32_t start; }; static int linux_ioctl_hdio(struct thread *td, struct linux_ioctl_args *args) { struct file *fp; int error; u_int sectorsize, fwcylinders, fwheads, fwsectors; off_t mediasize, bytespercyl; error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) return (error); switch (args->cmd & 0xffff) { case LINUX_HDIO_GET_GEO: case LINUX_HDIO_GET_GEO_BIG: error = fo_ioctl(fp, DIOCGMEDIASIZE, (caddr_t)&mediasize, td->td_ucred, td); if (!error) error = fo_ioctl(fp, DIOCGSECTORSIZE, (caddr_t)§orsize, td->td_ucred, td); if (!error) error = fo_ioctl(fp, DIOCGFWHEADS, (caddr_t)&fwheads, td->td_ucred, td); if (!error) error = fo_ioctl(fp, DIOCGFWSECTORS, (caddr_t)&fwsectors, td->td_ucred, td); /* * XXX: DIOCGFIRSTOFFSET is not yet implemented, so * so pretend that GEOM always says 0. This is NOT VALID * for slices or partitions, only the per-disk raw devices. */ fdrop(fp, td); if (error) return (error); /* * 1. Calculate the number of bytes in a cylinder, * given the firmware's notion of heads and sectors * per cylinder. * 2. Calculate the number of cylinders, given the total * size of the media. * All internal calculations should have 64-bit precision. */ bytespercyl = (off_t) sectorsize * fwheads * fwsectors; fwcylinders = mediasize / bytespercyl; #if defined(DEBUG) linux_msg(td, "HDIO_GET_GEO: mediasize %jd, c/h/s %d/%d/%d, " "bpc %jd", (intmax_t)mediasize, fwcylinders, fwheads, fwsectors, (intmax_t)bytespercyl); #endif if ((args->cmd & 0xffff) == LINUX_HDIO_GET_GEO) { struct linux_hd_geometry hdg; hdg.cylinders = fwcylinders; hdg.heads = fwheads; hdg.sectors = fwsectors; hdg.start = 0; error = copyout(&hdg, (void *)args->arg, sizeof(hdg)); } else if ((args->cmd & 0xffff) == LINUX_HDIO_GET_GEO_BIG) { struct linux_hd_big_geometry hdbg; memset(&hdbg, 0, sizeof(hdbg)); hdbg.cylinders = fwcylinders; hdbg.heads = fwheads; hdbg.sectors = fwsectors; hdbg.start = 0; error = copyout(&hdbg, (void *)args->arg, sizeof(hdbg)); } return (error); break; default: /* XXX */ linux_msg(td, "ioctl fd=%d, cmd=0x%x ('%c',%d) is not implemented", args->fd, (int)(args->cmd & 0xffff), (int)(args->cmd & 0xff00) >> 8, (int)(args->cmd & 0xff)); break; } fdrop(fp, td); return (ENOIOCTL); } static int linux_ioctl_disk(struct thread *td, struct linux_ioctl_args *args) { struct file *fp; int error; u_int sectorsize; off_t mediasize; error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) return (error); switch (args->cmd & 0xffff) { case LINUX_BLKGETSIZE: error = fo_ioctl(fp, DIOCGSECTORSIZE, (caddr_t)§orsize, td->td_ucred, td); if (!error) error = fo_ioctl(fp, DIOCGMEDIASIZE, (caddr_t)&mediasize, td->td_ucred, td); fdrop(fp, td); if (error) return (error); sectorsize = mediasize / sectorsize; /* * XXX: How do we know we return the right size of integer ? */ return (copyout(§orsize, (void *)args->arg, sizeof(sectorsize))); break; case LINUX_BLKSSZGET: error = fo_ioctl(fp, DIOCGSECTORSIZE, (caddr_t)§orsize, td->td_ucred, td); fdrop(fp, td); if (error) return (error); return (copyout(§orsize, (void *)args->arg, sizeof(sectorsize))); break; } fdrop(fp, td); return (ENOIOCTL); } /* * termio related ioctls */ struct linux_termio { unsigned short c_iflag; unsigned short c_oflag; unsigned short c_cflag; unsigned short c_lflag; unsigned char c_line; unsigned char c_cc[LINUX_NCC]; }; struct linux_termios { unsigned int c_iflag; unsigned int c_oflag; unsigned int c_cflag; unsigned int c_lflag; unsigned char c_line; unsigned char c_cc[LINUX_NCCS]; }; struct linux_winsize { unsigned short ws_row, ws_col; unsigned short ws_xpixel, ws_ypixel; }; struct speedtab { int sp_speed; /* Speed. */ int sp_code; /* Code. */ }; static struct speedtab sptab[] = { { B0, LINUX_B0 }, { B50, LINUX_B50 }, { B75, LINUX_B75 }, { B110, LINUX_B110 }, { B134, LINUX_B134 }, { B150, LINUX_B150 }, { B200, LINUX_B200 }, { B300, LINUX_B300 }, { B600, LINUX_B600 }, { B1200, LINUX_B1200 }, { B1800, LINUX_B1800 }, { B2400, LINUX_B2400 }, { B4800, LINUX_B4800 }, { B9600, LINUX_B9600 }, { B19200, LINUX_B19200 }, { B38400, LINUX_B38400 }, { B57600, LINUX_B57600 }, { B115200, LINUX_B115200 }, {-1, -1 } }; struct linux_serial_struct { int type; int line; int port; int irq; int flags; int xmit_fifo_size; int custom_divisor; int baud_base; unsigned short close_delay; char reserved_char[2]; int hub6; unsigned short closing_wait; unsigned short closing_wait2; int reserved[4]; }; static int linux_to_bsd_speed(int code, struct speedtab *table) { for ( ; table->sp_code != -1; table++) if (table->sp_code == code) return (table->sp_speed); return (-1); } static int bsd_to_linux_speed(int speed, struct speedtab *table) { for ( ; table->sp_speed != -1; table++) if (table->sp_speed == speed) return (table->sp_code); return (-1); } static void bsd_to_linux_termios(struct termios *bios, struct linux_termios *lios) { int i; #ifdef DEBUG if (ldebug(ioctl)) { printf("LINUX: BSD termios structure (input):\n"); printf("i=%08x o=%08x c=%08x l=%08x ispeed=%d ospeed=%d\n", bios->c_iflag, bios->c_oflag, bios->c_cflag, bios->c_lflag, bios->c_ispeed, bios->c_ospeed); printf("c_cc "); for (i=0; ic_cc[i]); printf("\n"); } #endif lios->c_iflag = 0; if (bios->c_iflag & IGNBRK) lios->c_iflag |= LINUX_IGNBRK; if (bios->c_iflag & BRKINT) lios->c_iflag |= LINUX_BRKINT; if (bios->c_iflag & IGNPAR) lios->c_iflag |= LINUX_IGNPAR; if (bios->c_iflag & PARMRK) lios->c_iflag |= LINUX_PARMRK; if (bios->c_iflag & INPCK) lios->c_iflag |= LINUX_INPCK; if (bios->c_iflag & ISTRIP) lios->c_iflag |= LINUX_ISTRIP; if (bios->c_iflag & INLCR) lios->c_iflag |= LINUX_INLCR; if (bios->c_iflag & IGNCR) lios->c_iflag |= LINUX_IGNCR; if (bios->c_iflag & ICRNL) lios->c_iflag |= LINUX_ICRNL; if (bios->c_iflag & IXON) lios->c_iflag |= LINUX_IXON; if (bios->c_iflag & IXANY) lios->c_iflag |= LINUX_IXANY; if (bios->c_iflag & IXOFF) lios->c_iflag |= LINUX_IXOFF; if (bios->c_iflag & IMAXBEL) lios->c_iflag |= LINUX_IMAXBEL; lios->c_oflag = 0; if (bios->c_oflag & OPOST) lios->c_oflag |= LINUX_OPOST; if (bios->c_oflag & ONLCR) lios->c_oflag |= LINUX_ONLCR; if (bios->c_oflag & TAB3) lios->c_oflag |= LINUX_XTABS; lios->c_cflag = bsd_to_linux_speed(bios->c_ispeed, sptab); lios->c_cflag |= (bios->c_cflag & CSIZE) >> 4; if (bios->c_cflag & CSTOPB) lios->c_cflag |= LINUX_CSTOPB; if (bios->c_cflag & CREAD) lios->c_cflag |= LINUX_CREAD; if (bios->c_cflag & PARENB) lios->c_cflag |= LINUX_PARENB; if (bios->c_cflag & PARODD) lios->c_cflag |= LINUX_PARODD; if (bios->c_cflag & HUPCL) lios->c_cflag |= LINUX_HUPCL; if (bios->c_cflag & CLOCAL) lios->c_cflag |= LINUX_CLOCAL; if (bios->c_cflag & CRTSCTS) lios->c_cflag |= LINUX_CRTSCTS; lios->c_lflag = 0; if (bios->c_lflag & ISIG) lios->c_lflag |= LINUX_ISIG; if (bios->c_lflag & ICANON) lios->c_lflag |= LINUX_ICANON; if (bios->c_lflag & ECHO) lios->c_lflag |= LINUX_ECHO; if (bios->c_lflag & ECHOE) lios->c_lflag |= LINUX_ECHOE; if (bios->c_lflag & ECHOK) lios->c_lflag |= LINUX_ECHOK; if (bios->c_lflag & ECHONL) lios->c_lflag |= LINUX_ECHONL; if (bios->c_lflag & NOFLSH) lios->c_lflag |= LINUX_NOFLSH; if (bios->c_lflag & TOSTOP) lios->c_lflag |= LINUX_TOSTOP; if (bios->c_lflag & ECHOCTL) lios->c_lflag |= LINUX_ECHOCTL; if (bios->c_lflag & ECHOPRT) lios->c_lflag |= LINUX_ECHOPRT; if (bios->c_lflag & ECHOKE) lios->c_lflag |= LINUX_ECHOKE; if (bios->c_lflag & FLUSHO) lios->c_lflag |= LINUX_FLUSHO; if (bios->c_lflag & PENDIN) lios->c_lflag |= LINUX_PENDIN; if (bios->c_lflag & IEXTEN) lios->c_lflag |= LINUX_IEXTEN; for (i=0; ic_cc[i] = LINUX_POSIX_VDISABLE; lios->c_cc[LINUX_VINTR] = bios->c_cc[VINTR]; lios->c_cc[LINUX_VQUIT] = bios->c_cc[VQUIT]; lios->c_cc[LINUX_VERASE] = bios->c_cc[VERASE]; lios->c_cc[LINUX_VKILL] = bios->c_cc[VKILL]; lios->c_cc[LINUX_VEOF] = bios->c_cc[VEOF]; lios->c_cc[LINUX_VEOL] = bios->c_cc[VEOL]; lios->c_cc[LINUX_VMIN] = bios->c_cc[VMIN]; lios->c_cc[LINUX_VTIME] = bios->c_cc[VTIME]; lios->c_cc[LINUX_VEOL2] = bios->c_cc[VEOL2]; lios->c_cc[LINUX_VSUSP] = bios->c_cc[VSUSP]; lios->c_cc[LINUX_VSTART] = bios->c_cc[VSTART]; lios->c_cc[LINUX_VSTOP] = bios->c_cc[VSTOP]; lios->c_cc[LINUX_VREPRINT] = bios->c_cc[VREPRINT]; lios->c_cc[LINUX_VDISCARD] = bios->c_cc[VDISCARD]; lios->c_cc[LINUX_VWERASE] = bios->c_cc[VWERASE]; lios->c_cc[LINUX_VLNEXT] = bios->c_cc[VLNEXT]; for (i=0; ic_cc[i] == _POSIX_VDISABLE) lios->c_cc[i] = LINUX_POSIX_VDISABLE; } lios->c_line = 0; #ifdef DEBUG if (ldebug(ioctl)) { printf("LINUX: LINUX termios structure (output):\n"); printf("i=%08x o=%08x c=%08x l=%08x line=%d\n", lios->c_iflag, lios->c_oflag, lios->c_cflag, lios->c_lflag, (int)lios->c_line); printf("c_cc "); for (i=0; ic_cc[i]); printf("\n"); } #endif } static void linux_to_bsd_termios(struct linux_termios *lios, struct termios *bios) { int i; #ifdef DEBUG if (ldebug(ioctl)) { printf("LINUX: LINUX termios structure (input):\n"); printf("i=%08x o=%08x c=%08x l=%08x line=%d\n", lios->c_iflag, lios->c_oflag, lios->c_cflag, lios->c_lflag, (int)lios->c_line); printf("c_cc "); for (i=0; ic_cc[i]); printf("\n"); } #endif bios->c_iflag = 0; if (lios->c_iflag & LINUX_IGNBRK) bios->c_iflag |= IGNBRK; if (lios->c_iflag & LINUX_BRKINT) bios->c_iflag |= BRKINT; if (lios->c_iflag & LINUX_IGNPAR) bios->c_iflag |= IGNPAR; if (lios->c_iflag & LINUX_PARMRK) bios->c_iflag |= PARMRK; if (lios->c_iflag & LINUX_INPCK) bios->c_iflag |= INPCK; if (lios->c_iflag & LINUX_ISTRIP) bios->c_iflag |= ISTRIP; if (lios->c_iflag & LINUX_INLCR) bios->c_iflag |= INLCR; if (lios->c_iflag & LINUX_IGNCR) bios->c_iflag |= IGNCR; if (lios->c_iflag & LINUX_ICRNL) bios->c_iflag |= ICRNL; if (lios->c_iflag & LINUX_IXON) bios->c_iflag |= IXON; if (lios->c_iflag & LINUX_IXANY) bios->c_iflag |= IXANY; if (lios->c_iflag & LINUX_IXOFF) bios->c_iflag |= IXOFF; if (lios->c_iflag & LINUX_IMAXBEL) bios->c_iflag |= IMAXBEL; bios->c_oflag = 0; if (lios->c_oflag & LINUX_OPOST) bios->c_oflag |= OPOST; if (lios->c_oflag & LINUX_ONLCR) bios->c_oflag |= ONLCR; if (lios->c_oflag & LINUX_XTABS) bios->c_oflag |= TAB3; bios->c_cflag = (lios->c_cflag & LINUX_CSIZE) << 4; if (lios->c_cflag & LINUX_CSTOPB) bios->c_cflag |= CSTOPB; if (lios->c_cflag & LINUX_CREAD) bios->c_cflag |= CREAD; if (lios->c_cflag & LINUX_PARENB) bios->c_cflag |= PARENB; if (lios->c_cflag & LINUX_PARODD) bios->c_cflag |= PARODD; if (lios->c_cflag & LINUX_HUPCL) bios->c_cflag |= HUPCL; if (lios->c_cflag & LINUX_CLOCAL) bios->c_cflag |= CLOCAL; if (lios->c_cflag & LINUX_CRTSCTS) bios->c_cflag |= CRTSCTS; bios->c_lflag = 0; if (lios->c_lflag & LINUX_ISIG) bios->c_lflag |= ISIG; if (lios->c_lflag & LINUX_ICANON) bios->c_lflag |= ICANON; if (lios->c_lflag & LINUX_ECHO) bios->c_lflag |= ECHO; if (lios->c_lflag & LINUX_ECHOE) bios->c_lflag |= ECHOE; if (lios->c_lflag & LINUX_ECHOK) bios->c_lflag |= ECHOK; if (lios->c_lflag & LINUX_ECHONL) bios->c_lflag |= ECHONL; if (lios->c_lflag & LINUX_NOFLSH) bios->c_lflag |= NOFLSH; if (lios->c_lflag & LINUX_TOSTOP) bios->c_lflag |= TOSTOP; if (lios->c_lflag & LINUX_ECHOCTL) bios->c_lflag |= ECHOCTL; if (lios->c_lflag & LINUX_ECHOPRT) bios->c_lflag |= ECHOPRT; if (lios->c_lflag & LINUX_ECHOKE) bios->c_lflag |= ECHOKE; if (lios->c_lflag & LINUX_FLUSHO) bios->c_lflag |= FLUSHO; if (lios->c_lflag & LINUX_PENDIN) bios->c_lflag |= PENDIN; if (lios->c_lflag & LINUX_IEXTEN) bios->c_lflag |= IEXTEN; for (i=0; ic_cc[i] = _POSIX_VDISABLE; bios->c_cc[VINTR] = lios->c_cc[LINUX_VINTR]; bios->c_cc[VQUIT] = lios->c_cc[LINUX_VQUIT]; bios->c_cc[VERASE] = lios->c_cc[LINUX_VERASE]; bios->c_cc[VKILL] = lios->c_cc[LINUX_VKILL]; bios->c_cc[VEOF] = lios->c_cc[LINUX_VEOF]; bios->c_cc[VEOL] = lios->c_cc[LINUX_VEOL]; bios->c_cc[VMIN] = lios->c_cc[LINUX_VMIN]; bios->c_cc[VTIME] = lios->c_cc[LINUX_VTIME]; bios->c_cc[VEOL2] = lios->c_cc[LINUX_VEOL2]; bios->c_cc[VSUSP] = lios->c_cc[LINUX_VSUSP]; bios->c_cc[VSTART] = lios->c_cc[LINUX_VSTART]; bios->c_cc[VSTOP] = lios->c_cc[LINUX_VSTOP]; bios->c_cc[VREPRINT] = lios->c_cc[LINUX_VREPRINT]; bios->c_cc[VDISCARD] = lios->c_cc[LINUX_VDISCARD]; bios->c_cc[VWERASE] = lios->c_cc[LINUX_VWERASE]; bios->c_cc[VLNEXT] = lios->c_cc[LINUX_VLNEXT]; for (i=0; ic_cc[i] == LINUX_POSIX_VDISABLE) bios->c_cc[i] = _POSIX_VDISABLE; } bios->c_ispeed = bios->c_ospeed = linux_to_bsd_speed(lios->c_cflag & LINUX_CBAUD, sptab); #ifdef DEBUG if (ldebug(ioctl)) { printf("LINUX: BSD termios structure (output):\n"); printf("i=%08x o=%08x c=%08x l=%08x ispeed=%d ospeed=%d\n", bios->c_iflag, bios->c_oflag, bios->c_cflag, bios->c_lflag, bios->c_ispeed, bios->c_ospeed); printf("c_cc "); for (i=0; ic_cc[i]); printf("\n"); } #endif } static void bsd_to_linux_termio(struct termios *bios, struct linux_termio *lio) { struct linux_termios lios; memset(lio, 0, sizeof(*lio)); bsd_to_linux_termios(bios, &lios); lio->c_iflag = lios.c_iflag; lio->c_oflag = lios.c_oflag; lio->c_cflag = lios.c_cflag; lio->c_lflag = lios.c_lflag; lio->c_line = lios.c_line; memcpy(lio->c_cc, lios.c_cc, LINUX_NCC); } static void linux_to_bsd_termio(struct linux_termio *lio, struct termios *bios) { struct linux_termios lios; int i; lios.c_iflag = lio->c_iflag; lios.c_oflag = lio->c_oflag; lios.c_cflag = lio->c_cflag; lios.c_lflag = lio->c_lflag; for (i=LINUX_NCC; ic_cc, LINUX_NCC); linux_to_bsd_termios(&lios, bios); } static int linux_ioctl_termio(struct thread *td, struct linux_ioctl_args *args) { struct termios bios; struct linux_termios lios; struct linux_termio lio; struct file *fp; int error; error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) return (error); switch (args->cmd & 0xffff) { case LINUX_TCGETS: error = fo_ioctl(fp, TIOCGETA, (caddr_t)&bios, td->td_ucred, td); if (error) break; bsd_to_linux_termios(&bios, &lios); error = copyout(&lios, (void *)args->arg, sizeof(lios)); break; case LINUX_TCSETS: error = copyin((void *)args->arg, &lios, sizeof(lios)); if (error) break; linux_to_bsd_termios(&lios, &bios); error = (fo_ioctl(fp, TIOCSETA, (caddr_t)&bios, td->td_ucred, td)); break; case LINUX_TCSETSW: error = copyin((void *)args->arg, &lios, sizeof(lios)); if (error) break; linux_to_bsd_termios(&lios, &bios); error = (fo_ioctl(fp, TIOCSETAW, (caddr_t)&bios, td->td_ucred, td)); break; case LINUX_TCSETSF: error = copyin((void *)args->arg, &lios, sizeof(lios)); if (error) break; linux_to_bsd_termios(&lios, &bios); error = (fo_ioctl(fp, TIOCSETAF, (caddr_t)&bios, td->td_ucred, td)); break; case LINUX_TCGETA: error = fo_ioctl(fp, TIOCGETA, (caddr_t)&bios, td->td_ucred, td); if (error) break; bsd_to_linux_termio(&bios, &lio); error = (copyout(&lio, (void *)args->arg, sizeof(lio))); break; case LINUX_TCSETA: error = copyin((void *)args->arg, &lio, sizeof(lio)); if (error) break; linux_to_bsd_termio(&lio, &bios); error = (fo_ioctl(fp, TIOCSETA, (caddr_t)&bios, td->td_ucred, td)); break; case LINUX_TCSETAW: error = copyin((void *)args->arg, &lio, sizeof(lio)); if (error) break; linux_to_bsd_termio(&lio, &bios); error = (fo_ioctl(fp, TIOCSETAW, (caddr_t)&bios, td->td_ucred, td)); break; case LINUX_TCSETAF: error = copyin((void *)args->arg, &lio, sizeof(lio)); if (error) break; linux_to_bsd_termio(&lio, &bios); error = (fo_ioctl(fp, TIOCSETAF, (caddr_t)&bios, td->td_ucred, td)); break; /* LINUX_TCSBRK */ case LINUX_TCXONC: { switch (args->arg) { case LINUX_TCOOFF: args->cmd = TIOCSTOP; break; case LINUX_TCOON: args->cmd = TIOCSTART; break; case LINUX_TCIOFF: case LINUX_TCION: { int c; struct write_args wr; error = fo_ioctl(fp, TIOCGETA, (caddr_t)&bios, td->td_ucred, td); if (error) break; fdrop(fp, td); c = (args->arg == LINUX_TCIOFF) ? VSTOP : VSTART; c = bios.c_cc[c]; if (c != _POSIX_VDISABLE) { wr.fd = args->fd; wr.buf = &c; wr.nbyte = sizeof(c); return (sys_write(td, &wr)); } else return (0); } default: fdrop(fp, td); return (EINVAL); } args->arg = 0; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; } case LINUX_TCFLSH: { int val; switch (args->arg) { case LINUX_TCIFLUSH: val = FREAD; break; case LINUX_TCOFLUSH: val = FWRITE; break; case LINUX_TCIOFLUSH: val = FREAD | FWRITE; break; default: fdrop(fp, td); return (EINVAL); } error = (fo_ioctl(fp,TIOCFLUSH,(caddr_t)&val,td->td_ucred,td)); break; } case LINUX_TIOCEXCL: args->cmd = TIOCEXCL; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCNXCL: args->cmd = TIOCNXCL; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCSCTTY: args->cmd = TIOCSCTTY; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCGPGRP: args->cmd = TIOCGPGRP; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCSPGRP: args->cmd = TIOCSPGRP; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; /* LINUX_TIOCOUTQ */ /* LINUX_TIOCSTI */ case LINUX_TIOCGWINSZ: args->cmd = TIOCGWINSZ; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCSWINSZ: args->cmd = TIOCSWINSZ; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCMGET: args->cmd = TIOCMGET; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCMBIS: args->cmd = TIOCMBIS; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCMBIC: args->cmd = TIOCMBIC; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCMSET: args->cmd = TIOCMSET; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; /* TIOCGSOFTCAR */ /* TIOCSSOFTCAR */ case LINUX_FIONREAD: /* LINUX_TIOCINQ */ args->cmd = FIONREAD; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; /* LINUX_TIOCLINUX */ case LINUX_TIOCCONS: args->cmd = TIOCCONS; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCGSERIAL: { struct linux_serial_struct lss; bzero(&lss, sizeof(lss)); lss.type = LINUX_PORT_16550A; lss.flags = 0; lss.close_delay = 0; error = copyout(&lss, (void *)args->arg, sizeof(lss)); break; } case LINUX_TIOCSSERIAL: { struct linux_serial_struct lss; error = copyin((void *)args->arg, &lss, sizeof(lss)); if (error) break; /* XXX - It really helps to have an implementation that * does nothing. NOT! */ error = 0; break; } case LINUX_TIOCPKT: args->cmd = TIOCPKT; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_FIONBIO: args->cmd = FIONBIO; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCNOTTY: args->cmd = TIOCNOTTY; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCSETD: { int line; switch (args->arg) { case LINUX_N_TTY: line = TTYDISC; break; case LINUX_N_SLIP: line = SLIPDISC; break; case LINUX_N_PPP: line = PPPDISC; break; default: fdrop(fp, td); return (EINVAL); } error = (fo_ioctl(fp, TIOCSETD, (caddr_t)&line, td->td_ucred, td)); break; } case LINUX_TIOCGETD: { int linux_line; int bsd_line = TTYDISC; error = fo_ioctl(fp, TIOCGETD, (caddr_t)&bsd_line, td->td_ucred, td); if (error) break; switch (bsd_line) { case TTYDISC: linux_line = LINUX_N_TTY; break; case SLIPDISC: linux_line = LINUX_N_SLIP; break; case PPPDISC: linux_line = LINUX_N_PPP; break; default: fdrop(fp, td); return (EINVAL); } error = (copyout(&linux_line, (void *)args->arg, sizeof(int))); break; } /* LINUX_TCSBRKP */ /* LINUX_TIOCTTYGSTRUCT */ case LINUX_FIONCLEX: args->cmd = FIONCLEX; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_FIOCLEX: args->cmd = FIOCLEX; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_FIOASYNC: args->cmd = FIOASYNC; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; /* LINUX_TIOCSERCONFIG */ /* LINUX_TIOCSERGWILD */ /* LINUX_TIOCSERSWILD */ /* LINUX_TIOCGLCKTRMIOS */ /* LINUX_TIOCSLCKTRMIOS */ case LINUX_TIOCSBRK: args->cmd = TIOCSBRK; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCCBRK: args->cmd = TIOCCBRK; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_TIOCGPTN: { int nb; error = fo_ioctl(fp, TIOCGPTN, (caddr_t)&nb, td->td_ucred, td); if (!error) error = copyout(&nb, (void *)args->arg, sizeof(int)); break; } case LINUX_TIOCSPTLCK: /* Our unlockpt() does nothing. */ error = 0; break; default: error = ENOIOCTL; break; } fdrop(fp, td); return (error); } /* * CDROM related ioctls */ struct linux_cdrom_msf { u_char cdmsf_min0; u_char cdmsf_sec0; u_char cdmsf_frame0; u_char cdmsf_min1; u_char cdmsf_sec1; u_char cdmsf_frame1; }; struct linux_cdrom_tochdr { u_char cdth_trk0; u_char cdth_trk1; }; union linux_cdrom_addr { struct { u_char minute; u_char second; u_char frame; } msf; int lba; }; struct linux_cdrom_tocentry { u_char cdte_track; u_char cdte_adr:4; u_char cdte_ctrl:4; u_char cdte_format; union linux_cdrom_addr cdte_addr; u_char cdte_datamode; }; struct linux_cdrom_subchnl { u_char cdsc_format; u_char cdsc_audiostatus; u_char cdsc_adr:4; u_char cdsc_ctrl:4; u_char cdsc_trk; u_char cdsc_ind; union linux_cdrom_addr cdsc_absaddr; union linux_cdrom_addr cdsc_reladdr; }; struct l_cdrom_read_audio { union linux_cdrom_addr addr; u_char addr_format; l_int nframes; u_char *buf; }; struct l_dvd_layer { u_char book_version:4; u_char book_type:4; u_char min_rate:4; u_char disc_size:4; u_char layer_type:4; u_char track_path:1; u_char nlayers:2; u_char track_density:4; u_char linear_density:4; u_char bca:1; u_int32_t start_sector; u_int32_t end_sector; u_int32_t end_sector_l0; }; struct l_dvd_physical { u_char type; u_char layer_num; struct l_dvd_layer layer[4]; }; struct l_dvd_copyright { u_char type; u_char layer_num; u_char cpst; u_char rmi; }; struct l_dvd_disckey { u_char type; l_uint agid:2; u_char value[2048]; }; struct l_dvd_bca { u_char type; l_int len; u_char value[188]; }; struct l_dvd_manufact { u_char type; u_char layer_num; l_int len; u_char value[2048]; }; typedef union { u_char type; struct l_dvd_physical physical; struct l_dvd_copyright copyright; struct l_dvd_disckey disckey; struct l_dvd_bca bca; struct l_dvd_manufact manufact; } l_dvd_struct; typedef u_char l_dvd_key[5]; typedef u_char l_dvd_challenge[10]; struct l_dvd_lu_send_agid { u_char type; l_uint agid:2; }; struct l_dvd_host_send_challenge { u_char type; l_uint agid:2; l_dvd_challenge chal; }; struct l_dvd_send_key { u_char type; l_uint agid:2; l_dvd_key key; }; struct l_dvd_lu_send_challenge { u_char type; l_uint agid:2; l_dvd_challenge chal; }; struct l_dvd_lu_send_title_key { u_char type; l_uint agid:2; l_dvd_key title_key; l_int lba; l_uint cpm:1; l_uint cp_sec:1; l_uint cgms:2; }; struct l_dvd_lu_send_asf { u_char type; l_uint agid:2; l_uint asf:1; }; struct l_dvd_host_send_rpcstate { u_char type; u_char pdrc; }; struct l_dvd_lu_send_rpcstate { u_char type:2; u_char vra:3; u_char ucca:3; u_char region_mask; u_char rpc_scheme; }; typedef union { u_char type; struct l_dvd_lu_send_agid lsa; struct l_dvd_host_send_challenge hsc; struct l_dvd_send_key lsk; struct l_dvd_lu_send_challenge lsc; struct l_dvd_send_key hsk; struct l_dvd_lu_send_title_key lstk; struct l_dvd_lu_send_asf lsasf; struct l_dvd_host_send_rpcstate hrpcs; struct l_dvd_lu_send_rpcstate lrpcs; } l_dvd_authinfo; static void bsd_to_linux_msf_lba(u_char af, union msf_lba *bp, union linux_cdrom_addr *lp) { if (af == CD_LBA_FORMAT) lp->lba = bp->lba; else { lp->msf.minute = bp->msf.minute; lp->msf.second = bp->msf.second; lp->msf.frame = bp->msf.frame; } } static void set_linux_cdrom_addr(union linux_cdrom_addr *addr, int format, int lba) { if (format == LINUX_CDROM_MSF) { addr->msf.frame = lba % 75; lba /= 75; lba += 2; addr->msf.second = lba % 60; addr->msf.minute = lba / 60; } else addr->lba = lba; } static int linux_to_bsd_dvd_struct(l_dvd_struct *lp, struct dvd_struct *bp) { bp->format = lp->type; switch (bp->format) { case DVD_STRUCT_PHYSICAL: if (bp->layer_num >= 4) return (EINVAL); bp->layer_num = lp->physical.layer_num; break; case DVD_STRUCT_COPYRIGHT: bp->layer_num = lp->copyright.layer_num; break; case DVD_STRUCT_DISCKEY: bp->agid = lp->disckey.agid; break; case DVD_STRUCT_BCA: case DVD_STRUCT_MANUFACT: break; default: return (EINVAL); } return (0); } static int bsd_to_linux_dvd_struct(struct dvd_struct *bp, l_dvd_struct *lp) { switch (bp->format) { case DVD_STRUCT_PHYSICAL: { struct dvd_layer *blp = (struct dvd_layer *)bp->data; struct l_dvd_layer *llp = &lp->physical.layer[bp->layer_num]; memset(llp, 0, sizeof(*llp)); llp->book_version = blp->book_version; llp->book_type = blp->book_type; llp->min_rate = blp->max_rate; llp->disc_size = blp->disc_size; llp->layer_type = blp->layer_type; llp->track_path = blp->track_path; llp->nlayers = blp->nlayers; llp->track_density = blp->track_density; llp->linear_density = blp->linear_density; llp->bca = blp->bca; llp->start_sector = blp->start_sector; llp->end_sector = blp->end_sector; llp->end_sector_l0 = blp->end_sector_l0; break; } case DVD_STRUCT_COPYRIGHT: lp->copyright.cpst = bp->cpst; lp->copyright.rmi = bp->rmi; break; case DVD_STRUCT_DISCKEY: memcpy(lp->disckey.value, bp->data, sizeof(lp->disckey.value)); break; case DVD_STRUCT_BCA: lp->bca.len = bp->length; memcpy(lp->bca.value, bp->data, sizeof(lp->bca.value)); break; case DVD_STRUCT_MANUFACT: lp->manufact.len = bp->length; memcpy(lp->manufact.value, bp->data, sizeof(lp->manufact.value)); /* lp->manufact.layer_num is unused in Linux (redhat 7.0). */ break; default: return (EINVAL); } return (0); } static int linux_to_bsd_dvd_authinfo(l_dvd_authinfo *lp, int *bcode, struct dvd_authinfo *bp) { switch (lp->type) { case LINUX_DVD_LU_SEND_AGID: *bcode = DVDIOCREPORTKEY; bp->format = DVD_REPORT_AGID; bp->agid = lp->lsa.agid; break; case LINUX_DVD_HOST_SEND_CHALLENGE: *bcode = DVDIOCSENDKEY; bp->format = DVD_SEND_CHALLENGE; bp->agid = lp->hsc.agid; memcpy(bp->keychal, lp->hsc.chal, 10); break; case LINUX_DVD_LU_SEND_KEY1: *bcode = DVDIOCREPORTKEY; bp->format = DVD_REPORT_KEY1; bp->agid = lp->lsk.agid; break; case LINUX_DVD_LU_SEND_CHALLENGE: *bcode = DVDIOCREPORTKEY; bp->format = DVD_REPORT_CHALLENGE; bp->agid = lp->lsc.agid; break; case LINUX_DVD_HOST_SEND_KEY2: *bcode = DVDIOCSENDKEY; bp->format = DVD_SEND_KEY2; bp->agid = lp->hsk.agid; memcpy(bp->keychal, lp->hsk.key, 5); break; case LINUX_DVD_LU_SEND_TITLE_KEY: *bcode = DVDIOCREPORTKEY; bp->format = DVD_REPORT_TITLE_KEY; bp->agid = lp->lstk.agid; bp->lba = lp->lstk.lba; break; case LINUX_DVD_LU_SEND_ASF: *bcode = DVDIOCREPORTKEY; bp->format = DVD_REPORT_ASF; bp->agid = lp->lsasf.agid; break; case LINUX_DVD_INVALIDATE_AGID: *bcode = DVDIOCREPORTKEY; bp->format = DVD_INVALIDATE_AGID; bp->agid = lp->lsa.agid; break; case LINUX_DVD_LU_SEND_RPC_STATE: *bcode = DVDIOCREPORTKEY; bp->format = DVD_REPORT_RPC; break; case LINUX_DVD_HOST_SEND_RPC_STATE: *bcode = DVDIOCSENDKEY; bp->format = DVD_SEND_RPC; bp->region = lp->hrpcs.pdrc; break; default: return (EINVAL); } return (0); } static int bsd_to_linux_dvd_authinfo(struct dvd_authinfo *bp, l_dvd_authinfo *lp) { switch (lp->type) { case LINUX_DVD_LU_SEND_AGID: lp->lsa.agid = bp->agid; break; case LINUX_DVD_HOST_SEND_CHALLENGE: lp->type = LINUX_DVD_LU_SEND_KEY1; break; case LINUX_DVD_LU_SEND_KEY1: memcpy(lp->lsk.key, bp->keychal, sizeof(lp->lsk.key)); break; case LINUX_DVD_LU_SEND_CHALLENGE: memcpy(lp->lsc.chal, bp->keychal, sizeof(lp->lsc.chal)); break; case LINUX_DVD_HOST_SEND_KEY2: lp->type = LINUX_DVD_AUTH_ESTABLISHED; break; case LINUX_DVD_LU_SEND_TITLE_KEY: memcpy(lp->lstk.title_key, bp->keychal, sizeof(lp->lstk.title_key)); lp->lstk.cpm = bp->cpm; lp->lstk.cp_sec = bp->cp_sec; lp->lstk.cgms = bp->cgms; break; case LINUX_DVD_LU_SEND_ASF: lp->lsasf.asf = bp->asf; break; case LINUX_DVD_INVALIDATE_AGID: break; case LINUX_DVD_LU_SEND_RPC_STATE: lp->lrpcs.type = bp->reg_type; lp->lrpcs.vra = bp->vend_rsts; lp->lrpcs.ucca = bp->user_rsts; lp->lrpcs.region_mask = bp->region; lp->lrpcs.rpc_scheme = bp->rpc_scheme; break; case LINUX_DVD_HOST_SEND_RPC_STATE: break; default: return (EINVAL); } return (0); } static int linux_ioctl_cdrom(struct thread *td, struct linux_ioctl_args *args) { struct file *fp; int error; error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) return (error); switch (args->cmd & 0xffff) { case LINUX_CDROMPAUSE: args->cmd = CDIOCPAUSE; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_CDROMRESUME: args->cmd = CDIOCRESUME; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_CDROMPLAYMSF: args->cmd = CDIOCPLAYMSF; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_CDROMPLAYTRKIND: args->cmd = CDIOCPLAYTRACKS; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_CDROMREADTOCHDR: { struct ioc_toc_header th; struct linux_cdrom_tochdr lth; error = fo_ioctl(fp, CDIOREADTOCHEADER, (caddr_t)&th, td->td_ucred, td); if (!error) { lth.cdth_trk0 = th.starting_track; lth.cdth_trk1 = th.ending_track; copyout(<h, (void *)args->arg, sizeof(lth)); } break; } case LINUX_CDROMREADTOCENTRY: { struct linux_cdrom_tocentry lte; struct ioc_read_toc_single_entry irtse; error = copyin((void *)args->arg, <e, sizeof(lte)); if (error) break; irtse.address_format = lte.cdte_format; irtse.track = lte.cdte_track; error = fo_ioctl(fp, CDIOREADTOCENTRY, (caddr_t)&irtse, td->td_ucred, td); if (!error) { lte.cdte_ctrl = irtse.entry.control; lte.cdte_adr = irtse.entry.addr_type; bsd_to_linux_msf_lba(irtse.address_format, &irtse.entry.addr, <e.cdte_addr); error = copyout(<e, (void *)args->arg, sizeof(lte)); } break; } case LINUX_CDROMSTOP: args->cmd = CDIOCSTOP; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_CDROMSTART: args->cmd = CDIOCSTART; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_CDROMEJECT: args->cmd = CDIOCEJECT; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; /* LINUX_CDROMVOLCTRL */ case LINUX_CDROMSUBCHNL: { struct linux_cdrom_subchnl sc; struct ioc_read_subchannel bsdsc; struct cd_sub_channel_info bsdinfo; error = copyin((void *)args->arg, &sc, sizeof(sc)); if (error) break; /* * Invoke the native ioctl and bounce the returned data through * the userspace buffer. This works because the Linux structure * is the same size as our structures for the subchannel header * and position data. */ bsdsc.address_format = CD_LBA_FORMAT; bsdsc.data_format = CD_CURRENT_POSITION; bsdsc.track = 0; bsdsc.data_len = sizeof(sc); bsdsc.data = (void *)args->arg; error = fo_ioctl(fp, CDIOCREADSUBCHANNEL, (caddr_t)&bsdsc, td->td_ucred, td); if (error) break; error = copyin((void *)args->arg, &bsdinfo, sizeof(bsdinfo)); if (error) break; sc.cdsc_audiostatus = bsdinfo.header.audio_status; sc.cdsc_adr = bsdinfo.what.position.addr_type; sc.cdsc_ctrl = bsdinfo.what.position.control; sc.cdsc_trk = bsdinfo.what.position.track_number; sc.cdsc_ind = bsdinfo.what.position.index_number; set_linux_cdrom_addr(&sc.cdsc_absaddr, sc.cdsc_format, bsdinfo.what.position.absaddr.lba); set_linux_cdrom_addr(&sc.cdsc_reladdr, sc.cdsc_format, bsdinfo.what.position.reladdr.lba); error = copyout(&sc, (void *)args->arg, sizeof(sc)); break; } /* LINUX_CDROMREADMODE2 */ /* LINUX_CDROMREADMODE1 */ /* LINUX_CDROMREADAUDIO */ /* LINUX_CDROMEJECT_SW */ /* LINUX_CDROMMULTISESSION */ /* LINUX_CDROM_GET_UPC */ case LINUX_CDROMRESET: args->cmd = CDIOCRESET; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; /* LINUX_CDROMVOLREAD */ /* LINUX_CDROMREADRAW */ /* LINUX_CDROMREADCOOKED */ /* LINUX_CDROMSEEK */ /* LINUX_CDROMPLAYBLK */ /* LINUX_CDROMREADALL */ /* LINUX_CDROMCLOSETRAY */ /* LINUX_CDROMLOADFROMSLOT */ /* LINUX_CDROMGETSPINDOWN */ /* LINUX_CDROMSETSPINDOWN */ /* LINUX_CDROM_SET_OPTIONS */ /* LINUX_CDROM_CLEAR_OPTIONS */ /* LINUX_CDROM_SELECT_SPEED */ /* LINUX_CDROM_SELECT_DISC */ /* LINUX_CDROM_MEDIA_CHANGED */ /* LINUX_CDROM_DRIVE_STATUS */ /* LINUX_CDROM_DISC_STATUS */ /* LINUX_CDROM_CHANGER_NSLOTS */ /* LINUX_CDROM_LOCKDOOR */ /* LINUX_CDROM_DEBUG */ /* LINUX_CDROM_GET_CAPABILITY */ /* LINUX_CDROMAUDIOBUFSIZ */ case LINUX_DVD_READ_STRUCT: { l_dvd_struct *lds; struct dvd_struct *bds; lds = malloc(sizeof(*lds), M_LINUX, M_WAITOK); bds = malloc(sizeof(*bds), M_LINUX, M_WAITOK); error = copyin((void *)args->arg, lds, sizeof(*lds)); if (error) goto out; error = linux_to_bsd_dvd_struct(lds, bds); if (error) goto out; error = fo_ioctl(fp, DVDIOCREADSTRUCTURE, (caddr_t)bds, td->td_ucred, td); if (error) goto out; error = bsd_to_linux_dvd_struct(bds, lds); if (error) goto out; error = copyout(lds, (void *)args->arg, sizeof(*lds)); out: free(bds, M_LINUX); free(lds, M_LINUX); break; } /* LINUX_DVD_WRITE_STRUCT */ case LINUX_DVD_AUTH: { l_dvd_authinfo lda; struct dvd_authinfo bda; int bcode; error = copyin((void *)args->arg, &lda, sizeof(lda)); if (error) break; error = linux_to_bsd_dvd_authinfo(&lda, &bcode, &bda); if (error) break; error = fo_ioctl(fp, bcode, (caddr_t)&bda, td->td_ucred, td); if (error) { if (lda.type == LINUX_DVD_HOST_SEND_KEY2) { lda.type = LINUX_DVD_AUTH_FAILURE; copyout(&lda, (void *)args->arg, sizeof(lda)); } break; } error = bsd_to_linux_dvd_authinfo(&bda, &lda); if (error) break; error = copyout(&lda, (void *)args->arg, sizeof(lda)); break; } case LINUX_SCSI_GET_BUS_NUMBER: { struct sg_scsi_id id; error = fo_ioctl(fp, SG_GET_SCSI_ID, (caddr_t)&id, td->td_ucred, td); if (error) break; error = copyout(&id.channel, (void *)args->arg, sizeof(int)); break; } case LINUX_SCSI_GET_IDLUN: { struct sg_scsi_id id; struct scsi_idlun idl; error = fo_ioctl(fp, SG_GET_SCSI_ID, (caddr_t)&id, td->td_ucred, td); if (error) break; idl.dev_id = (id.scsi_id & 0xff) + ((id.lun & 0xff) << 8) + ((id.channel & 0xff) << 16) + ((id.host_no & 0xff) << 24); idl.host_unique_id = id.host_no; error = copyout(&idl, (void *)args->arg, sizeof(idl)); break; } /* LINUX_CDROM_SEND_PACKET */ /* LINUX_CDROM_NEXT_WRITABLE */ /* LINUX_CDROM_LAST_WRITTEN */ default: error = ENOIOCTL; break; } fdrop(fp, td); return (error); } static int linux_ioctl_vfat(struct thread *td, struct linux_ioctl_args *args) { return (ENOTTY); } /* * Sound related ioctls */ struct linux_old_mixer_info { char id[16]; char name[32]; }; static u_int32_t dirbits[4] = { IOC_VOID, IOC_IN, IOC_OUT, IOC_INOUT }; #define SETDIR(c) (((c) & ~IOC_DIRMASK) | dirbits[args->cmd >> 30]) static int linux_ioctl_sound(struct thread *td, struct linux_ioctl_args *args) { switch (args->cmd & 0xffff) { case LINUX_SOUND_MIXER_WRITE_VOLUME: args->cmd = SETDIR(SOUND_MIXER_WRITE_VOLUME); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_BASS: args->cmd = SETDIR(SOUND_MIXER_WRITE_BASS); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_TREBLE: args->cmd = SETDIR(SOUND_MIXER_WRITE_TREBLE); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_SYNTH: args->cmd = SETDIR(SOUND_MIXER_WRITE_SYNTH); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_PCM: args->cmd = SETDIR(SOUND_MIXER_WRITE_PCM); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_SPEAKER: args->cmd = SETDIR(SOUND_MIXER_WRITE_SPEAKER); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_LINE: args->cmd = SETDIR(SOUND_MIXER_WRITE_LINE); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_MIC: args->cmd = SETDIR(SOUND_MIXER_WRITE_MIC); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_CD: args->cmd = SETDIR(SOUND_MIXER_WRITE_CD); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_IMIX: args->cmd = SETDIR(SOUND_MIXER_WRITE_IMIX); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_ALTPCM: args->cmd = SETDIR(SOUND_MIXER_WRITE_ALTPCM); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_RECLEV: args->cmd = SETDIR(SOUND_MIXER_WRITE_RECLEV); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_IGAIN: args->cmd = SETDIR(SOUND_MIXER_WRITE_IGAIN); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_OGAIN: args->cmd = SETDIR(SOUND_MIXER_WRITE_OGAIN); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_LINE1: args->cmd = SETDIR(SOUND_MIXER_WRITE_LINE1); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_LINE2: args->cmd = SETDIR(SOUND_MIXER_WRITE_LINE2); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_LINE3: args->cmd = SETDIR(SOUND_MIXER_WRITE_LINE3); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_INFO: { /* Key on encoded length */ switch ((args->cmd >> 16) & 0x1fff) { case 0x005c: { /* SOUND_MIXER_INFO */ args->cmd = SOUND_MIXER_INFO; return (sys_ioctl(td, (struct ioctl_args *)args)); } case 0x0030: { /* SOUND_OLD_MIXER_INFO */ struct linux_old_mixer_info info; bzero(&info, sizeof(info)); strncpy(info.id, "OSS", sizeof(info.id) - 1); strncpy(info.name, "FreeBSD OSS Mixer", sizeof(info.name) - 1); copyout(&info, (void *)args->arg, sizeof(info)); return (0); } default: return (ENOIOCTL); } break; } case LINUX_OSS_GETVERSION: { int version = linux_get_oss_version(td); return (copyout(&version, (void *)args->arg, sizeof(int))); } case LINUX_SOUND_MIXER_READ_STEREODEVS: args->cmd = SOUND_MIXER_READ_STEREODEVS; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_READ_CAPS: args->cmd = SOUND_MIXER_READ_CAPS; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_READ_RECMASK: args->cmd = SOUND_MIXER_READ_RECMASK; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_READ_DEVMASK: args->cmd = SOUND_MIXER_READ_DEVMASK; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_MIXER_WRITE_RECSRC: args->cmd = SETDIR(SOUND_MIXER_WRITE_RECSRC); return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_RESET: args->cmd = SNDCTL_DSP_RESET; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_SYNC: args->cmd = SNDCTL_DSP_SYNC; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_SPEED: args->cmd = SNDCTL_DSP_SPEED; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_STEREO: args->cmd = SNDCTL_DSP_STEREO; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_GETBLKSIZE: /* LINUX_SNDCTL_DSP_SETBLKSIZE */ args->cmd = SNDCTL_DSP_GETBLKSIZE; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_SETFMT: args->cmd = SNDCTL_DSP_SETFMT; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_PCM_WRITE_CHANNELS: args->cmd = SOUND_PCM_WRITE_CHANNELS; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SOUND_PCM_WRITE_FILTER: args->cmd = SOUND_PCM_WRITE_FILTER; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_POST: args->cmd = SNDCTL_DSP_POST; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_SUBDIVIDE: args->cmd = SNDCTL_DSP_SUBDIVIDE; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_SETFRAGMENT: args->cmd = SNDCTL_DSP_SETFRAGMENT; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_GETFMTS: args->cmd = SNDCTL_DSP_GETFMTS; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_GETOSPACE: args->cmd = SNDCTL_DSP_GETOSPACE; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_GETISPACE: args->cmd = SNDCTL_DSP_GETISPACE; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_NONBLOCK: args->cmd = SNDCTL_DSP_NONBLOCK; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_GETCAPS: args->cmd = SNDCTL_DSP_GETCAPS; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_SETTRIGGER: /* LINUX_SNDCTL_GETTRIGGER */ args->cmd = SNDCTL_DSP_SETTRIGGER; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_GETIPTR: args->cmd = SNDCTL_DSP_GETIPTR; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_GETOPTR: args->cmd = SNDCTL_DSP_GETOPTR; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_SETDUPLEX: args->cmd = SNDCTL_DSP_SETDUPLEX; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_DSP_GETODELAY: args->cmd = SNDCTL_DSP_GETODELAY; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_RESET: args->cmd = SNDCTL_SEQ_RESET; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_SYNC: args->cmd = SNDCTL_SEQ_SYNC; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SYNTH_INFO: args->cmd = SNDCTL_SYNTH_INFO; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_CTRLRATE: args->cmd = SNDCTL_SEQ_CTRLRATE; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_GETOUTCOUNT: args->cmd = SNDCTL_SEQ_GETOUTCOUNT; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_GETINCOUNT: args->cmd = SNDCTL_SEQ_GETINCOUNT; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_PERCMODE: args->cmd = SNDCTL_SEQ_PERCMODE; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_FM_LOAD_INSTR: args->cmd = SNDCTL_FM_LOAD_INSTR; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_TESTMIDI: args->cmd = SNDCTL_SEQ_TESTMIDI; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_RESETSAMPLES: args->cmd = SNDCTL_SEQ_RESETSAMPLES; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_NRSYNTHS: args->cmd = SNDCTL_SEQ_NRSYNTHS; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_NRMIDIS: args->cmd = SNDCTL_SEQ_NRMIDIS; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_MIDI_INFO: args->cmd = SNDCTL_MIDI_INFO; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SEQ_TRESHOLD: args->cmd = SNDCTL_SEQ_TRESHOLD; return (sys_ioctl(td, (struct ioctl_args *)args)); case LINUX_SNDCTL_SYNTH_MEMAVL: args->cmd = SNDCTL_SYNTH_MEMAVL; return (sys_ioctl(td, (struct ioctl_args *)args)); } return (ENOIOCTL); } /* * Console related ioctls */ static int linux_ioctl_console(struct thread *td, struct linux_ioctl_args *args) { struct file *fp; int error; error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) return (error); switch (args->cmd & 0xffff) { case LINUX_KIOCSOUND: args->cmd = KIOCSOUND; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_KDMKTONE: args->cmd = KDMKTONE; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_KDGETLED: args->cmd = KDGETLED; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_KDSETLED: args->cmd = KDSETLED; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_KDSETMODE: args->cmd = KDSETMODE; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_KDGETMODE: args->cmd = KDGETMODE; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_KDGKBMODE: args->cmd = KDGKBMODE; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_KDSKBMODE: { int kbdmode; switch (args->arg) { case LINUX_KBD_RAW: kbdmode = K_RAW; break; case LINUX_KBD_XLATE: kbdmode = K_XLATE; break; case LINUX_KBD_MEDIUMRAW: kbdmode = K_RAW; break; default: fdrop(fp, td); return (EINVAL); } error = (fo_ioctl(fp, KDSKBMODE, (caddr_t)&kbdmode, td->td_ucred, td)); break; } case LINUX_VT_OPENQRY: args->cmd = VT_OPENQRY; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_VT_GETMODE: args->cmd = VT_GETMODE; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_VT_SETMODE: { struct vt_mode mode; if ((error = copyin((void *)args->arg, &mode, sizeof(mode)))) break; if (LINUX_SIG_VALID(mode.relsig)) mode.relsig = linux_to_bsd_signal(mode.relsig); else mode.relsig = 0; if (LINUX_SIG_VALID(mode.acqsig)) mode.acqsig = linux_to_bsd_signal(mode.acqsig); else mode.acqsig = 0; /* XXX. Linux ignores frsig and set it to 0. */ mode.frsig = 0; if ((error = copyout(&mode, (void *)args->arg, sizeof(mode)))) break; args->cmd = VT_SETMODE; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; } case LINUX_VT_GETSTATE: args->cmd = VT_GETACTIVE; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_VT_RELDISP: args->cmd = VT_RELDISP; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_VT_ACTIVATE: args->cmd = VT_ACTIVATE; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; case LINUX_VT_WAITACTIVE: args->cmd = VT_WAITACTIVE; error = (sys_ioctl(td, (struct ioctl_args *)args)); break; default: error = ENOIOCTL; break; } fdrop(fp, td); return (error); } /* * Implement the SIOCGIFNAME ioctl */ static int linux_ioctl_ifname(struct thread *td, struct l_ifreq *uifr) { struct l_ifreq ifr; struct ifnet *ifp; int error, ethno, index; error = copyin(uifr, &ifr, sizeof(ifr)); if (error != 0) return (error); CURVNET_SET(TD_TO_VNET(curthread)); IFNET_RLOCK(); index = 1; /* ifr.ifr_ifindex starts from 1 */ ethno = 0; error = ENODEV; CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { if (ifr.ifr_ifindex == index) { if (IFP_IS_ETH(ifp)) snprintf(ifr.ifr_name, LINUX_IFNAMSIZ, "eth%d", ethno); else strlcpy(ifr.ifr_name, ifp->if_xname, LINUX_IFNAMSIZ); error = 0; break; } if (IFP_IS_ETH(ifp)) ethno++; index++; } IFNET_RUNLOCK(); if (error == 0) error = copyout(&ifr, uifr, sizeof(ifr)); CURVNET_RESTORE(); return (error); } /* * Implement the SIOCGIFCONF ioctl */ static int linux_ifconf(struct thread *td, struct ifconf *uifc) { #ifdef COMPAT_LINUX32 struct l_ifconf ifc; #else struct ifconf ifc; #endif struct l_ifreq ifr; struct ifnet *ifp; struct ifaddr *ifa; struct sbuf *sb; int error, ethno, full = 0, valid_len, max_len; error = copyin(uifc, &ifc, sizeof(ifc)); if (error != 0) return (error); max_len = MAXPHYS - 1; CURVNET_SET(TD_TO_VNET(td)); /* handle the 'request buffer size' case */ if ((l_uintptr_t)ifc.ifc_buf == PTROUT(NULL)) { ifc.ifc_len = 0; IFNET_RLOCK(); CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { struct sockaddr *sa = ifa->ifa_addr; if (sa->sa_family == AF_INET) ifc.ifc_len += sizeof(ifr); } } IFNET_RUNLOCK(); error = copyout(&ifc, uifc, sizeof(ifc)); CURVNET_RESTORE(); return (error); } if (ifc.ifc_len <= 0) { CURVNET_RESTORE(); return (EINVAL); } again: /* Keep track of eth interfaces */ ethno = 0; if (ifc.ifc_len <= max_len) { max_len = ifc.ifc_len; full = 1; } sb = sbuf_new(NULL, NULL, max_len + 1, SBUF_FIXEDLEN); max_len = 0; valid_len = 0; /* Return all AF_INET addresses of all interfaces */ IFNET_RLOCK(); CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { int addrs = 0; bzero(&ifr, sizeof(ifr)); if (IFP_IS_ETH(ifp)) snprintf(ifr.ifr_name, LINUX_IFNAMSIZ, "eth%d", ethno++); else strlcpy(ifr.ifr_name, ifp->if_xname, LINUX_IFNAMSIZ); /* Walk the address list */ CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { struct sockaddr *sa = ifa->ifa_addr; if (sa->sa_family == AF_INET) { ifr.ifr_addr.sa_family = LINUX_AF_INET; memcpy(ifr.ifr_addr.sa_data, sa->sa_data, sizeof(ifr.ifr_addr.sa_data)); sbuf_bcat(sb, &ifr, sizeof(ifr)); max_len += sizeof(ifr); addrs++; } if (sbuf_error(sb) == 0) valid_len = sbuf_len(sb); } if (addrs == 0) { bzero((caddr_t)&ifr.ifr_addr, sizeof(ifr.ifr_addr)); sbuf_bcat(sb, &ifr, sizeof(ifr)); max_len += sizeof(ifr); if (sbuf_error(sb) == 0) valid_len = sbuf_len(sb); } } IFNET_RUNLOCK(); if (valid_len != max_len && !full) { sbuf_delete(sb); goto again; } ifc.ifc_len = valid_len; sbuf_finish(sb); error = copyout(sbuf_data(sb), PTRIN(ifc.ifc_buf), ifc.ifc_len); if (error == 0) error = copyout(&ifc, uifc, sizeof(ifc)); sbuf_delete(sb); CURVNET_RESTORE(); return (error); } static int linux_gifflags(struct thread *td, struct ifnet *ifp, struct l_ifreq *ifr) { l_short flags; linux_ifflags(ifp, &flags); return (copyout(&flags, &ifr->ifr_flags, sizeof(flags))); } static int linux_gifhwaddr(struct ifnet *ifp, struct l_ifreq *ifr) { struct l_sockaddr lsa; if (linux_ifhwaddr(ifp, &lsa) != 0) return (ENOENT); return (copyout(&lsa, &ifr->ifr_hwaddr, sizeof(lsa))); } /* * If we fault in bsd_to_linux_ifreq() then we will fault when we call * the native ioctl(). Thus, we don't really need to check the return * value of this function. */ static int bsd_to_linux_ifreq(struct ifreq *arg) { struct ifreq ifr; size_t ifr_len = sizeof(struct ifreq); int error; if ((error = copyin(arg, &ifr, ifr_len))) return (error); *(u_short *)&ifr.ifr_addr = ifr.ifr_addr.sa_family; error = copyout(&ifr, arg, ifr_len); return (error); } /* * Socket related ioctls */ static int linux_ioctl_socket(struct thread *td, struct linux_ioctl_args *args) { char lifname[LINUX_IFNAMSIZ], ifname[IFNAMSIZ]; struct ifnet *ifp; struct file *fp; int error, type; ifp = NULL; error = 0; error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) return (error); type = fp->f_type; fdrop(fp, td); if (type != DTYPE_SOCKET) { /* not a socket - probably a tap / vmnet device */ switch (args->cmd) { case LINUX_SIOCGIFADDR: case LINUX_SIOCSIFADDR: case LINUX_SIOCGIFFLAGS: return (linux_ioctl_special(td, args)); default: return (ENOIOCTL); } } switch (args->cmd & 0xffff) { case LINUX_FIOGETOWN: case LINUX_FIOSETOWN: case LINUX_SIOCADDMULTI: case LINUX_SIOCATMARK: case LINUX_SIOCDELMULTI: case LINUX_SIOCGIFNAME: case LINUX_SIOCGIFCONF: case LINUX_SIOCGPGRP: case LINUX_SIOCSPGRP: case LINUX_SIOCGIFCOUNT: /* these ioctls don't take an interface name */ #ifdef DEBUG printf("%s(): ioctl %d\n", __func__, args->cmd & 0xffff); #endif break; case LINUX_SIOCGIFFLAGS: case LINUX_SIOCGIFADDR: case LINUX_SIOCSIFADDR: case LINUX_SIOCGIFDSTADDR: case LINUX_SIOCGIFBRDADDR: case LINUX_SIOCGIFNETMASK: case LINUX_SIOCSIFNETMASK: case LINUX_SIOCGIFMTU: case LINUX_SIOCSIFMTU: case LINUX_SIOCSIFNAME: case LINUX_SIOCGIFHWADDR: case LINUX_SIOCSIFHWADDR: case LINUX_SIOCDEVPRIVATE: case LINUX_SIOCDEVPRIVATE+1: case LINUX_SIOCGIFINDEX: /* copy in the interface name and translate it. */ error = copyin((void *)args->arg, lifname, LINUX_IFNAMSIZ); if (error != 0) return (error); #ifdef DEBUG printf("%s(): ioctl %d on %.*s\n", __func__, args->cmd & 0xffff, LINUX_IFNAMSIZ, lifname); #endif memset(ifname, 0, sizeof(ifname)); ifp = ifname_linux_to_bsd(td, lifname, ifname); if (ifp == NULL) return (EINVAL); /* * We need to copy it back out in case we pass the * request on to our native ioctl(), which will expect * the ifreq to be in user space and have the correct * interface name. */ error = copyout(ifname, (void *)args->arg, IFNAMSIZ); if (error != 0) return (error); #ifdef DEBUG printf("%s(): %s translated to %s\n", __func__, lifname, ifname); #endif break; default: return (ENOIOCTL); } switch (args->cmd & 0xffff) { case LINUX_FIOSETOWN: args->cmd = FIOSETOWN; error = sys_ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCSPGRP: args->cmd = SIOCSPGRP; error = sys_ioctl(td, (struct ioctl_args *)args); break; case LINUX_FIOGETOWN: args->cmd = FIOGETOWN; error = sys_ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCGPGRP: args->cmd = SIOCGPGRP; error = sys_ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCATMARK: args->cmd = SIOCATMARK; error = sys_ioctl(td, (struct ioctl_args *)args); break; /* LINUX_SIOCGSTAMP */ case LINUX_SIOCGIFNAME: error = linux_ioctl_ifname(td, (struct l_ifreq *)args->arg); break; case LINUX_SIOCGIFCONF: error = linux_ifconf(td, (struct ifconf *)args->arg); break; case LINUX_SIOCGIFFLAGS: args->cmd = SIOCGIFFLAGS; error = linux_gifflags(td, ifp, (struct l_ifreq *)args->arg); break; case LINUX_SIOCGIFADDR: args->cmd = SIOCGIFADDR; error = sys_ioctl(td, (struct ioctl_args *)args); bsd_to_linux_ifreq((struct ifreq *)args->arg); break; case LINUX_SIOCSIFADDR: /* XXX probably doesn't work, included for completeness */ args->cmd = SIOCSIFADDR; error = sys_ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCGIFDSTADDR: args->cmd = SIOCGIFDSTADDR; error = sys_ioctl(td, (struct ioctl_args *)args); bsd_to_linux_ifreq((struct ifreq *)args->arg); break; case LINUX_SIOCGIFBRDADDR: args->cmd = SIOCGIFBRDADDR; error = sys_ioctl(td, (struct ioctl_args *)args); bsd_to_linux_ifreq((struct ifreq *)args->arg); break; case LINUX_SIOCGIFNETMASK: args->cmd = SIOCGIFNETMASK; error = sys_ioctl(td, (struct ioctl_args *)args); bsd_to_linux_ifreq((struct ifreq *)args->arg); break; case LINUX_SIOCSIFNETMASK: error = ENOIOCTL; break; case LINUX_SIOCGIFMTU: args->cmd = SIOCGIFMTU; error = sys_ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCSIFMTU: args->cmd = SIOCSIFMTU; error = sys_ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCSIFNAME: error = ENOIOCTL; break; case LINUX_SIOCGIFHWADDR: error = linux_gifhwaddr(ifp, (struct l_ifreq *)args->arg); break; case LINUX_SIOCSIFHWADDR: error = ENOIOCTL; break; case LINUX_SIOCADDMULTI: args->cmd = SIOCADDMULTI; error = sys_ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCDELMULTI: args->cmd = SIOCDELMULTI; error = sys_ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCGIFINDEX: args->cmd = SIOCGIFINDEX; error = sys_ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCGIFCOUNT: error = 0; break; /* * XXX This is slightly bogus, but these ioctls are currently * XXX only used by the aironet (if_an) network driver. */ case LINUX_SIOCDEVPRIVATE: args->cmd = SIOCGPRIVATE_0; error = sys_ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCDEVPRIVATE+1: args->cmd = SIOCGPRIVATE_1; error = sys_ioctl(td, (struct ioctl_args *)args); break; } if (ifp != NULL) /* restore the original interface name */ copyout(lifname, (void *)args->arg, LINUX_IFNAMSIZ); #ifdef DEBUG printf("%s(): returning %d\n", __func__, error); #endif return (error); } /* * Device private ioctl handler */ static int linux_ioctl_private(struct thread *td, struct linux_ioctl_args *args) { struct file *fp; int error, type; error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) return (error); type = fp->f_type; fdrop(fp, td); if (type == DTYPE_SOCKET) return (linux_ioctl_socket(td, args)); return (ENOIOCTL); } /* * DRM ioctl handler (sys/dev/drm) */ static int linux_ioctl_drm(struct thread *td, struct linux_ioctl_args *args) { args->cmd = SETDIR(args->cmd); return (sys_ioctl(td, (struct ioctl_args *)args)); } #ifdef COMPAT_LINUX32 -#define CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0) -#define PTRIN_CP(src,dst,fld) \ - do { (dst).fld = PTRIN((src).fld); } while (0) -#define PTROUT_CP(src,dst,fld) \ - do { (dst).fld = PTROUT((src).fld); } while (0) - static int linux_ioctl_sg_io(struct thread *td, struct linux_ioctl_args *args) { struct sg_io_hdr io; struct sg_io_hdr32 io32; struct file *fp; int error; error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) { printf("sg_linux_ioctl: fget returned %d\n", error); return (error); } if ((error = copyin((void *)args->arg, &io32, sizeof(io32))) != 0) goto out; CP(io32, io, interface_id); CP(io32, io, dxfer_direction); CP(io32, io, cmd_len); CP(io32, io, mx_sb_len); CP(io32, io, iovec_count); CP(io32, io, dxfer_len); PTRIN_CP(io32, io, dxferp); PTRIN_CP(io32, io, cmdp); PTRIN_CP(io32, io, sbp); CP(io32, io, timeout); CP(io32, io, flags); CP(io32, io, pack_id); PTRIN_CP(io32, io, usr_ptr); CP(io32, io, status); CP(io32, io, masked_status); CP(io32, io, msg_status); CP(io32, io, sb_len_wr); CP(io32, io, host_status); CP(io32, io, driver_status); CP(io32, io, resid); CP(io32, io, duration); CP(io32, io, info); if ((error = fo_ioctl(fp, SG_IO, (caddr_t)&io, td->td_ucred, td)) != 0) goto out; CP(io, io32, interface_id); CP(io, io32, dxfer_direction); CP(io, io32, cmd_len); CP(io, io32, mx_sb_len); CP(io, io32, iovec_count); CP(io, io32, dxfer_len); PTROUT_CP(io, io32, dxferp); PTROUT_CP(io, io32, cmdp); PTROUT_CP(io, io32, sbp); CP(io, io32, timeout); CP(io, io32, flags); CP(io, io32, pack_id); PTROUT_CP(io, io32, usr_ptr); CP(io, io32, status); CP(io, io32, masked_status); CP(io, io32, msg_status); CP(io, io32, sb_len_wr); CP(io, io32, host_status); CP(io, io32, driver_status); CP(io, io32, resid); CP(io, io32, duration); CP(io, io32, info); error = copyout(&io32, (void *)args->arg, sizeof(io32)); out: fdrop(fp, td); return (error); } #endif static int linux_ioctl_sg(struct thread *td, struct linux_ioctl_args *args) { switch (args->cmd) { case LINUX_SG_GET_VERSION_NUM: args->cmd = SG_GET_VERSION_NUM; break; case LINUX_SG_SET_TIMEOUT: args->cmd = SG_SET_TIMEOUT; break; case LINUX_SG_GET_TIMEOUT: args->cmd = SG_GET_TIMEOUT; break; case LINUX_SG_IO: args->cmd = SG_IO; #ifdef COMPAT_LINUX32 return (linux_ioctl_sg_io(td, args)); #endif break; case LINUX_SG_GET_RESERVED_SIZE: args->cmd = SG_GET_RESERVED_SIZE; break; case LINUX_SG_GET_SCSI_ID: args->cmd = SG_GET_SCSI_ID; break; case LINUX_SG_GET_SG_TABLESIZE: args->cmd = SG_GET_SG_TABLESIZE; break; default: return (ENODEV); } return (sys_ioctl(td, (struct ioctl_args *)args)); } /* * Video4Linux (V4L) ioctl handler */ static int linux_to_bsd_v4l_tuner(struct l_video_tuner *lvt, struct video_tuner *vt) { vt->tuner = lvt->tuner; strlcpy(vt->name, lvt->name, LINUX_VIDEO_TUNER_NAME_SIZE); vt->rangelow = lvt->rangelow; /* possible long size conversion */ vt->rangehigh = lvt->rangehigh; /* possible long size conversion */ vt->flags = lvt->flags; vt->mode = lvt->mode; vt->signal = lvt->signal; return (0); } static int bsd_to_linux_v4l_tuner(struct video_tuner *vt, struct l_video_tuner *lvt) { lvt->tuner = vt->tuner; strlcpy(lvt->name, vt->name, LINUX_VIDEO_TUNER_NAME_SIZE); lvt->rangelow = vt->rangelow; /* possible long size conversion */ lvt->rangehigh = vt->rangehigh; /* possible long size conversion */ lvt->flags = vt->flags; lvt->mode = vt->mode; lvt->signal = vt->signal; return (0); } #ifdef COMPAT_LINUX_V4L_CLIPLIST static int linux_to_bsd_v4l_clip(struct l_video_clip *lvc, struct video_clip *vc) { vc->x = lvc->x; vc->y = lvc->y; vc->width = lvc->width; vc->height = lvc->height; vc->next = PTRIN(lvc->next); /* possible pointer size conversion */ return (0); } #endif static int linux_to_bsd_v4l_window(struct l_video_window *lvw, struct video_window *vw) { vw->x = lvw->x; vw->y = lvw->y; vw->width = lvw->width; vw->height = lvw->height; vw->chromakey = lvw->chromakey; vw->flags = lvw->flags; vw->clips = PTRIN(lvw->clips); /* possible pointer size conversion */ vw->clipcount = lvw->clipcount; return (0); } static int bsd_to_linux_v4l_window(struct video_window *vw, struct l_video_window *lvw) { memset(lvw, 0, sizeof(*lvw)); lvw->x = vw->x; lvw->y = vw->y; lvw->width = vw->width; lvw->height = vw->height; lvw->chromakey = vw->chromakey; lvw->flags = vw->flags; lvw->clips = PTROUT(vw->clips); /* possible pointer size conversion */ lvw->clipcount = vw->clipcount; return (0); } static int linux_to_bsd_v4l_buffer(struct l_video_buffer *lvb, struct video_buffer *vb) { vb->base = PTRIN(lvb->base); /* possible pointer size conversion */ vb->height = lvb->height; vb->width = lvb->width; vb->depth = lvb->depth; vb->bytesperline = lvb->bytesperline; return (0); } static int bsd_to_linux_v4l_buffer(struct video_buffer *vb, struct l_video_buffer *lvb) { lvb->base = PTROUT(vb->base); /* possible pointer size conversion */ lvb->height = vb->height; lvb->width = vb->width; lvb->depth = vb->depth; lvb->bytesperline = vb->bytesperline; return (0); } static int linux_to_bsd_v4l_code(struct l_video_code *lvc, struct video_code *vc) { strlcpy(vc->loadwhat, lvc->loadwhat, LINUX_VIDEO_CODE_LOADWHAT_SIZE); vc->datasize = lvc->datasize; vc->data = PTRIN(lvc->data); /* possible pointer size conversion */ return (0); } #ifdef COMPAT_LINUX_V4L_CLIPLIST static int linux_v4l_clip_copy(void *lvc, struct video_clip **ppvc) { int error; struct video_clip vclip; struct l_video_clip l_vclip; error = copyin(lvc, &l_vclip, sizeof(l_vclip)); if (error) return (error); linux_to_bsd_v4l_clip(&l_vclip, &vclip); /* XXX: If there can be no concurrency: s/M_NOWAIT/M_WAITOK/ */ if ((*ppvc = malloc(sizeof(**ppvc), M_LINUX, M_NOWAIT)) == NULL) return (ENOMEM); /* XXX: Linux has no ENOMEM here. */ memcpy(*ppvc, &vclip, sizeof(vclip)); (*ppvc)->next = NULL; return (0); } static int linux_v4l_cliplist_free(struct video_window *vw) { struct video_clip **ppvc; struct video_clip **ppvc_next; for (ppvc = &(vw->clips); *ppvc != NULL; ppvc = ppvc_next) { ppvc_next = &((*ppvc)->next); free(*ppvc, M_LINUX); } vw->clips = NULL; return (0); } static int linux_v4l_cliplist_copy(struct l_video_window *lvw, struct video_window *vw) { int error; int clipcount; void *plvc; struct video_clip **ppvc; /* * XXX: The cliplist is used to pass in a list of clipping * rectangles or, if clipcount == VIDEO_CLIP_BITMAP, a * clipping bitmap. Some Linux apps, however, appear to * leave cliplist and clips uninitialized. In any case, * the cliplist is not used by pwc(4), at the time of * writing, FreeBSD's only V4L driver. When a driver * that uses the cliplist is developed, this code may * need re-examiniation. */ error = 0; clipcount = vw->clipcount; if (clipcount == VIDEO_CLIP_BITMAP) { /* * In this case, the pointer (clips) is overloaded * to be a "void *" to a bitmap, therefore there * is no struct video_clip to copy now. */ } else if (clipcount > 0 && clipcount <= 16384) { /* * Clips points to list of clip rectangles, so * copy the list. * * XXX: Upper limit of 16384 was used here to try to * avoid cases when clipcount and clips pointer * are uninitialized and therefore have high random * values, as is the case in the Linux Skype * application. The value 16384 was chosen as that * is what is used in the Linux stradis(4) MPEG * decoder driver, the only place we found an * example of cliplist use. */ plvc = PTRIN(lvw->clips); vw->clips = NULL; ppvc = &(vw->clips); while (clipcount-- > 0) { if (plvc == NULL) { error = EFAULT; break; } else { error = linux_v4l_clip_copy(plvc, ppvc); if (error) { linux_v4l_cliplist_free(vw); break; } } ppvc = &((*ppvc)->next); plvc = PTRIN(((struct l_video_clip *) plvc)->next); } } else { /* * clipcount == 0 or negative (but not VIDEO_CLIP_BITMAP) * Force cliplist to null. */ vw->clipcount = 0; vw->clips = NULL; } return (error); } #endif static int linux_ioctl_v4l(struct thread *td, struct linux_ioctl_args *args) { struct file *fp; int error; struct video_tuner vtun; struct video_window vwin; struct video_buffer vbuf; struct video_code vcode; struct l_video_tuner l_vtun; struct l_video_window l_vwin; struct l_video_buffer l_vbuf; struct l_video_code l_vcode; switch (args->cmd & 0xffff) { case LINUX_VIDIOCGCAP: args->cmd = VIDIOCGCAP; break; case LINUX_VIDIOCGCHAN: args->cmd = VIDIOCGCHAN; break; case LINUX_VIDIOCSCHAN: args->cmd = VIDIOCSCHAN; break; case LINUX_VIDIOCGTUNER: error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) return (error); error = copyin((void *) args->arg, &l_vtun, sizeof(l_vtun)); if (error) { fdrop(fp, td); return (error); } linux_to_bsd_v4l_tuner(&l_vtun, &vtun); error = fo_ioctl(fp, VIDIOCGTUNER, &vtun, td->td_ucred, td); if (!error) { bsd_to_linux_v4l_tuner(&vtun, &l_vtun); error = copyout(&l_vtun, (void *) args->arg, sizeof(l_vtun)); } fdrop(fp, td); return (error); case LINUX_VIDIOCSTUNER: error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) return (error); error = copyin((void *) args->arg, &l_vtun, sizeof(l_vtun)); if (error) { fdrop(fp, td); return (error); } linux_to_bsd_v4l_tuner(&l_vtun, &vtun); error = fo_ioctl(fp, VIDIOCSTUNER, &vtun, td->td_ucred, td); fdrop(fp, td); return (error); case LINUX_VIDIOCGPICT: args->cmd = VIDIOCGPICT; break; case LINUX_VIDIOCSPICT: args->cmd = VIDIOCSPICT; break; case LINUX_VIDIOCCAPTURE: args->cmd = VIDIOCCAPTURE; break; case LINUX_VIDIOCGWIN: error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) return (error); error = fo_ioctl(fp, VIDIOCGWIN, &vwin, td->td_ucred, td); if (!error) { bsd_to_linux_v4l_window(&vwin, &l_vwin); error = copyout(&l_vwin, (void *) args->arg, sizeof(l_vwin)); } fdrop(fp, td); return (error); case LINUX_VIDIOCSWIN: error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) return (error); error = copyin((void *) args->arg, &l_vwin, sizeof(l_vwin)); if (error) { fdrop(fp, td); return (error); } linux_to_bsd_v4l_window(&l_vwin, &vwin); #ifdef COMPAT_LINUX_V4L_CLIPLIST error = linux_v4l_cliplist_copy(&l_vwin, &vwin); if (error) { fdrop(fp, td); return (error); } #endif error = fo_ioctl(fp, VIDIOCSWIN, &vwin, td->td_ucred, td); fdrop(fp, td); #ifdef COMPAT_LINUX_V4L_CLIPLIST linux_v4l_cliplist_free(&vwin); #endif return (error); case LINUX_VIDIOCGFBUF: error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) return (error); error = fo_ioctl(fp, VIDIOCGFBUF, &vbuf, td->td_ucred, td); if (!error) { bsd_to_linux_v4l_buffer(&vbuf, &l_vbuf); error = copyout(&l_vbuf, (void *) args->arg, sizeof(l_vbuf)); } fdrop(fp, td); return (error); case LINUX_VIDIOCSFBUF: error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) return (error); error = copyin((void *) args->arg, &l_vbuf, sizeof(l_vbuf)); if (error) { fdrop(fp, td); return (error); } linux_to_bsd_v4l_buffer(&l_vbuf, &vbuf); error = fo_ioctl(fp, VIDIOCSFBUF, &vbuf, td->td_ucred, td); fdrop(fp, td); return (error); case LINUX_VIDIOCKEY: args->cmd = VIDIOCKEY; break; case LINUX_VIDIOCGFREQ: args->cmd = VIDIOCGFREQ; break; case LINUX_VIDIOCSFREQ: args->cmd = VIDIOCSFREQ; break; case LINUX_VIDIOCGAUDIO: args->cmd = VIDIOCGAUDIO; break; case LINUX_VIDIOCSAUDIO: args->cmd = VIDIOCSAUDIO; break; case LINUX_VIDIOCSYNC: args->cmd = VIDIOCSYNC; break; case LINUX_VIDIOCMCAPTURE: args->cmd = VIDIOCMCAPTURE; break; case LINUX_VIDIOCGMBUF: args->cmd = VIDIOCGMBUF; break; case LINUX_VIDIOCGUNIT: args->cmd = VIDIOCGUNIT; break; case LINUX_VIDIOCGCAPTURE: args->cmd = VIDIOCGCAPTURE; break; case LINUX_VIDIOCSCAPTURE: args->cmd = VIDIOCSCAPTURE; break; case LINUX_VIDIOCSPLAYMODE: args->cmd = VIDIOCSPLAYMODE; break; case LINUX_VIDIOCSWRITEMODE: args->cmd = VIDIOCSWRITEMODE; break; case LINUX_VIDIOCGPLAYINFO: args->cmd = VIDIOCGPLAYINFO; break; case LINUX_VIDIOCSMICROCODE: error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) return (error); error = copyin((void *) args->arg, &l_vcode, sizeof(l_vcode)); if (error) { fdrop(fp, td); return (error); } linux_to_bsd_v4l_code(&l_vcode, &vcode); error = fo_ioctl(fp, VIDIOCSMICROCODE, &vcode, td->td_ucred, td); fdrop(fp, td); return (error); case LINUX_VIDIOCGVBIFMT: args->cmd = VIDIOCGVBIFMT; break; case LINUX_VIDIOCSVBIFMT: args->cmd = VIDIOCSVBIFMT; break; default: return (ENOIOCTL); } error = sys_ioctl(td, (struct ioctl_args *)args); return (error); } /* * Special ioctl handler */ static int linux_ioctl_special(struct thread *td, struct linux_ioctl_args *args) { int error; switch (args->cmd) { case LINUX_SIOCGIFADDR: args->cmd = SIOCGIFADDR; error = sys_ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCSIFADDR: args->cmd = SIOCSIFADDR; error = sys_ioctl(td, (struct ioctl_args *)args); break; case LINUX_SIOCGIFFLAGS: args->cmd = SIOCGIFFLAGS; error = sys_ioctl(td, (struct ioctl_args *)args); break; default: error = ENOIOCTL; } return (error); } static int linux_to_bsd_v4l2_standard(struct l_v4l2_standard *lvstd, struct v4l2_standard *vstd) { vstd->index = lvstd->index; vstd->id = lvstd->id; CTASSERT(sizeof(vstd->name) == sizeof(lvstd->name)); memcpy(vstd->name, lvstd->name, sizeof(vstd->name)); vstd->frameperiod = lvstd->frameperiod; vstd->framelines = lvstd->framelines; CTASSERT(sizeof(vstd->reserved) == sizeof(lvstd->reserved)); memcpy(vstd->reserved, lvstd->reserved, sizeof(vstd->reserved)); return (0); } static int bsd_to_linux_v4l2_standard(struct v4l2_standard *vstd, struct l_v4l2_standard *lvstd) { lvstd->index = vstd->index; lvstd->id = vstd->id; CTASSERT(sizeof(vstd->name) == sizeof(lvstd->name)); memcpy(lvstd->name, vstd->name, sizeof(lvstd->name)); lvstd->frameperiod = vstd->frameperiod; lvstd->framelines = vstd->framelines; CTASSERT(sizeof(vstd->reserved) == sizeof(lvstd->reserved)); memcpy(lvstd->reserved, vstd->reserved, sizeof(lvstd->reserved)); return (0); } static int linux_to_bsd_v4l2_buffer(struct l_v4l2_buffer *lvb, struct v4l2_buffer *vb) { vb->index = lvb->index; vb->type = lvb->type; vb->bytesused = lvb->bytesused; vb->flags = lvb->flags; vb->field = lvb->field; vb->timestamp.tv_sec = lvb->timestamp.tv_sec; vb->timestamp.tv_usec = lvb->timestamp.tv_usec; memcpy(&vb->timecode, &lvb->timecode, sizeof (lvb->timecode)); vb->sequence = lvb->sequence; vb->memory = lvb->memory; if (lvb->memory == V4L2_MEMORY_USERPTR) /* possible pointer size conversion */ vb->m.userptr = (unsigned long)PTRIN(lvb->m.userptr); else vb->m.offset = lvb->m.offset; vb->length = lvb->length; vb->input = lvb->input; vb->reserved = lvb->reserved; return (0); } static int bsd_to_linux_v4l2_buffer(struct v4l2_buffer *vb, struct l_v4l2_buffer *lvb) { lvb->index = vb->index; lvb->type = vb->type; lvb->bytesused = vb->bytesused; lvb->flags = vb->flags; lvb->field = vb->field; lvb->timestamp.tv_sec = vb->timestamp.tv_sec; lvb->timestamp.tv_usec = vb->timestamp.tv_usec; memcpy(&lvb->timecode, &vb->timecode, sizeof (vb->timecode)); lvb->sequence = vb->sequence; lvb->memory = vb->memory; if (vb->memory == V4L2_MEMORY_USERPTR) /* possible pointer size conversion */ lvb->m.userptr = PTROUT(vb->m.userptr); else lvb->m.offset = vb->m.offset; lvb->length = vb->length; lvb->input = vb->input; lvb->reserved = vb->reserved; return (0); } static int linux_to_bsd_v4l2_format(struct l_v4l2_format *lvf, struct v4l2_format *vf) { vf->type = lvf->type; if (lvf->type == V4L2_BUF_TYPE_VIDEO_OVERLAY #ifdef V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY || lvf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY #endif ) /* * XXX TODO - needs 32 -> 64 bit conversion: * (unused by webcams?) */ return (EINVAL); memcpy(&vf->fmt, &lvf->fmt, sizeof(vf->fmt)); return (0); } static int bsd_to_linux_v4l2_format(struct v4l2_format *vf, struct l_v4l2_format *lvf) { lvf->type = vf->type; if (vf->type == V4L2_BUF_TYPE_VIDEO_OVERLAY #ifdef V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY || vf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY #endif ) /* * XXX TODO - needs 32 -> 64 bit conversion: * (unused by webcams?) */ return (EINVAL); memcpy(&lvf->fmt, &vf->fmt, sizeof(vf->fmt)); return (0); } static int linux_ioctl_v4l2(struct thread *td, struct linux_ioctl_args *args) { struct file *fp; int error; struct v4l2_format vformat; struct l_v4l2_format l_vformat; struct v4l2_standard vstd; struct l_v4l2_standard l_vstd; struct l_v4l2_buffer l_vbuf; struct v4l2_buffer vbuf; struct v4l2_input vinp; switch (args->cmd & 0xffff) { case LINUX_VIDIOC_RESERVED: case LINUX_VIDIOC_LOG_STATUS: if ((args->cmd & IOC_DIRMASK) != LINUX_IOC_VOID) return (ENOIOCTL); args->cmd = (args->cmd & 0xffff) | IOC_VOID; break; case LINUX_VIDIOC_OVERLAY: case LINUX_VIDIOC_STREAMON: case LINUX_VIDIOC_STREAMOFF: case LINUX_VIDIOC_S_STD: case LINUX_VIDIOC_S_TUNER: case LINUX_VIDIOC_S_AUDIO: case LINUX_VIDIOC_S_AUDOUT: case LINUX_VIDIOC_S_MODULATOR: case LINUX_VIDIOC_S_FREQUENCY: case LINUX_VIDIOC_S_CROP: case LINUX_VIDIOC_S_JPEGCOMP: case LINUX_VIDIOC_S_PRIORITY: case LINUX_VIDIOC_DBG_S_REGISTER: case LINUX_VIDIOC_S_HW_FREQ_SEEK: case LINUX_VIDIOC_SUBSCRIBE_EVENT: case LINUX_VIDIOC_UNSUBSCRIBE_EVENT: args->cmd = (args->cmd & ~IOC_DIRMASK) | IOC_IN; break; case LINUX_VIDIOC_QUERYCAP: case LINUX_VIDIOC_G_STD: case LINUX_VIDIOC_G_AUDIO: case LINUX_VIDIOC_G_INPUT: case LINUX_VIDIOC_G_OUTPUT: case LINUX_VIDIOC_G_AUDOUT: case LINUX_VIDIOC_G_JPEGCOMP: case LINUX_VIDIOC_QUERYSTD: case LINUX_VIDIOC_G_PRIORITY: case LINUX_VIDIOC_QUERY_DV_PRESET: args->cmd = (args->cmd & ~IOC_DIRMASK) | IOC_OUT; break; case LINUX_VIDIOC_ENUM_FMT: case LINUX_VIDIOC_REQBUFS: case LINUX_VIDIOC_G_PARM: case LINUX_VIDIOC_S_PARM: case LINUX_VIDIOC_G_CTRL: case LINUX_VIDIOC_S_CTRL: case LINUX_VIDIOC_G_TUNER: case LINUX_VIDIOC_QUERYCTRL: case LINUX_VIDIOC_QUERYMENU: case LINUX_VIDIOC_S_INPUT: case LINUX_VIDIOC_S_OUTPUT: case LINUX_VIDIOC_ENUMOUTPUT: case LINUX_VIDIOC_G_MODULATOR: case LINUX_VIDIOC_G_FREQUENCY: case LINUX_VIDIOC_CROPCAP: case LINUX_VIDIOC_G_CROP: case LINUX_VIDIOC_ENUMAUDIO: case LINUX_VIDIOC_ENUMAUDOUT: case LINUX_VIDIOC_G_SLICED_VBI_CAP: #ifdef VIDIOC_ENUM_FRAMESIZES case LINUX_VIDIOC_ENUM_FRAMESIZES: case LINUX_VIDIOC_ENUM_FRAMEINTERVALS: case LINUX_VIDIOC_ENCODER_CMD: case LINUX_VIDIOC_TRY_ENCODER_CMD: #endif case LINUX_VIDIOC_DBG_G_REGISTER: case LINUX_VIDIOC_DBG_G_CHIP_IDENT: case LINUX_VIDIOC_ENUM_DV_PRESETS: case LINUX_VIDIOC_S_DV_PRESET: case LINUX_VIDIOC_G_DV_PRESET: case LINUX_VIDIOC_S_DV_TIMINGS: case LINUX_VIDIOC_G_DV_TIMINGS: args->cmd = (args->cmd & ~IOC_DIRMASK) | IOC_INOUT; break; case LINUX_VIDIOC_G_FMT: case LINUX_VIDIOC_S_FMT: case LINUX_VIDIOC_TRY_FMT: error = copyin((void *)args->arg, &l_vformat, sizeof(l_vformat)); if (error) return (error); error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error) return (error); if (linux_to_bsd_v4l2_format(&l_vformat, &vformat) != 0) error = EINVAL; else if ((args->cmd & 0xffff) == LINUX_VIDIOC_G_FMT) error = fo_ioctl(fp, VIDIOC_G_FMT, &vformat, td->td_ucred, td); else if ((args->cmd & 0xffff) == LINUX_VIDIOC_S_FMT) error = fo_ioctl(fp, VIDIOC_S_FMT, &vformat, td->td_ucred, td); else error = fo_ioctl(fp, VIDIOC_TRY_FMT, &vformat, td->td_ucred, td); bsd_to_linux_v4l2_format(&vformat, &l_vformat); copyout(&l_vformat, (void *)args->arg, sizeof(l_vformat)); fdrop(fp, td); return (error); case LINUX_VIDIOC_ENUMSTD: error = copyin((void *)args->arg, &l_vstd, sizeof(l_vstd)); if (error) return (error); linux_to_bsd_v4l2_standard(&l_vstd, &vstd); error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error) return (error); error = fo_ioctl(fp, VIDIOC_ENUMSTD, (caddr_t)&vstd, td->td_ucred, td); if (error) { fdrop(fp, td); return (error); } bsd_to_linux_v4l2_standard(&vstd, &l_vstd); error = copyout(&l_vstd, (void *)args->arg, sizeof(l_vstd)); fdrop(fp, td); return (error); case LINUX_VIDIOC_ENUMINPUT: /* * The Linux struct l_v4l2_input differs only in size, * it has no padding at the end. */ error = copyin((void *)args->arg, &vinp, sizeof(struct l_v4l2_input)); if (error != 0) return (error); error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) return (error); error = fo_ioctl(fp, VIDIOC_ENUMINPUT, (caddr_t)&vinp, td->td_ucred, td); if (error) { fdrop(fp, td); return (error); } error = copyout(&vinp, (void *)args->arg, sizeof(struct l_v4l2_input)); fdrop(fp, td); return (error); case LINUX_VIDIOC_QUERYBUF: case LINUX_VIDIOC_QBUF: case LINUX_VIDIOC_DQBUF: error = copyin((void *)args->arg, &l_vbuf, sizeof(l_vbuf)); if (error) return (error); error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error) return (error); linux_to_bsd_v4l2_buffer(&l_vbuf, &vbuf); if ((args->cmd & 0xffff) == LINUX_VIDIOC_QUERYBUF) error = fo_ioctl(fp, VIDIOC_QUERYBUF, &vbuf, td->td_ucred, td); else if ((args->cmd & 0xffff) == LINUX_VIDIOC_QBUF) error = fo_ioctl(fp, VIDIOC_QBUF, &vbuf, td->td_ucred, td); else error = fo_ioctl(fp, VIDIOC_DQBUF, &vbuf, td->td_ucred, td); bsd_to_linux_v4l2_buffer(&vbuf, &l_vbuf); copyout(&l_vbuf, (void *)args->arg, sizeof(l_vbuf)); fdrop(fp, td); return (error); /* * XXX TODO - these need 32 -> 64 bit conversion: * (are any of them needed for webcams?) */ case LINUX_VIDIOC_G_FBUF: case LINUX_VIDIOC_S_FBUF: case LINUX_VIDIOC_G_EXT_CTRLS: case LINUX_VIDIOC_S_EXT_CTRLS: case LINUX_VIDIOC_TRY_EXT_CTRLS: case LINUX_VIDIOC_DQEVENT: default: return (ENOIOCTL); } error = sys_ioctl(td, (struct ioctl_args *)args); return (error); } /* * Support for emulators/linux-libusb. This port uses FBSD_LUSB* macros * instead of USB* ones. This lets us to provide correct values for cmd. * 0xffffffe0 -- 0xffffffff range seemed to be the least collision-prone. */ static int linux_ioctl_fbsd_usb(struct thread *td, struct linux_ioctl_args *args) { int error; error = 0; switch (args->cmd) { case FBSD_LUSB_DEVICEENUMERATE: args->cmd = USB_DEVICEENUMERATE; break; case FBSD_LUSB_DEV_QUIRK_ADD: args->cmd = USB_DEV_QUIRK_ADD; break; case FBSD_LUSB_DEV_QUIRK_GET: args->cmd = USB_DEV_QUIRK_GET; break; case FBSD_LUSB_DEV_QUIRK_REMOVE: args->cmd = USB_DEV_QUIRK_REMOVE; break; case FBSD_LUSB_DO_REQUEST: args->cmd = USB_DO_REQUEST; break; case FBSD_LUSB_FS_CLEAR_STALL_SYNC: args->cmd = USB_FS_CLEAR_STALL_SYNC; break; case FBSD_LUSB_FS_CLOSE: args->cmd = USB_FS_CLOSE; break; case FBSD_LUSB_FS_COMPLETE: args->cmd = USB_FS_COMPLETE; break; case FBSD_LUSB_FS_INIT: args->cmd = USB_FS_INIT; break; case FBSD_LUSB_FS_OPEN: args->cmd = USB_FS_OPEN; break; case FBSD_LUSB_FS_START: args->cmd = USB_FS_START; break; case FBSD_LUSB_FS_STOP: args->cmd = USB_FS_STOP; break; case FBSD_LUSB_FS_UNINIT: args->cmd = USB_FS_UNINIT; break; case FBSD_LUSB_GET_CONFIG: args->cmd = USB_GET_CONFIG; break; case FBSD_LUSB_GET_DEVICEINFO: args->cmd = USB_GET_DEVICEINFO; break; case FBSD_LUSB_GET_DEVICE_DESC: args->cmd = USB_GET_DEVICE_DESC; break; case FBSD_LUSB_GET_FULL_DESC: args->cmd = USB_GET_FULL_DESC; break; case FBSD_LUSB_GET_IFACE_DRIVER: args->cmd = USB_GET_IFACE_DRIVER; break; case FBSD_LUSB_GET_PLUGTIME: args->cmd = USB_GET_PLUGTIME; break; case FBSD_LUSB_GET_POWER_MODE: args->cmd = USB_GET_POWER_MODE; break; case FBSD_LUSB_GET_REPORT_DESC: args->cmd = USB_GET_REPORT_DESC; break; case FBSD_LUSB_GET_REPORT_ID: args->cmd = USB_GET_REPORT_ID; break; case FBSD_LUSB_GET_TEMPLATE: args->cmd = USB_GET_TEMPLATE; break; case FBSD_LUSB_IFACE_DRIVER_ACTIVE: args->cmd = USB_IFACE_DRIVER_ACTIVE; break; case FBSD_LUSB_IFACE_DRIVER_DETACH: args->cmd = USB_IFACE_DRIVER_DETACH; break; case FBSD_LUSB_QUIRK_NAME_GET: args->cmd = USB_QUIRK_NAME_GET; break; case FBSD_LUSB_READ_DIR: args->cmd = USB_READ_DIR; break; case FBSD_LUSB_SET_ALTINTERFACE: args->cmd = USB_SET_ALTINTERFACE; break; case FBSD_LUSB_SET_CONFIG: args->cmd = USB_SET_CONFIG; break; case FBSD_LUSB_SET_IMMED: args->cmd = USB_SET_IMMED; break; case FBSD_LUSB_SET_POWER_MODE: args->cmd = USB_SET_POWER_MODE; break; case FBSD_LUSB_SET_TEMPLATE: args->cmd = USB_SET_TEMPLATE; break; case FBSD_LUSB_FS_OPEN_STREAM: args->cmd = USB_FS_OPEN_STREAM; break; case FBSD_LUSB_GET_DEV_PORT_PATH: args->cmd = USB_GET_DEV_PORT_PATH; break; case FBSD_LUSB_GET_POWER_USAGE: args->cmd = USB_GET_POWER_USAGE; break; case FBSD_LUSB_DEVICESTATS: args->cmd = USB_DEVICESTATS; break; default: error = ENOIOCTL; } if (error != ENOIOCTL) error = sys_ioctl(td, (struct ioctl_args *)args); return (error); } /* * Some evdev ioctls must be translated. * - EVIOCGMTSLOTS is a IOC_READ ioctl on Linux although it has input data * (must be IOC_INOUT on FreeBSD). * - On Linux, EVIOCGRAB, EVIOCREVOKE and EVIOCRMFF are defined as _IOW with * an int argument. You don't pass an int pointer to the ioctl(), however, * but just the int directly. On FreeBSD, they are defined as _IOWINT for * this to work. */ static int linux_ioctl_evdev(struct thread *td, struct linux_ioctl_args *args) { struct file *fp; clockid_t clock; int error; args->cmd = SETDIR(args->cmd); switch (args->cmd) { case (EVIOCGRAB & ~IOC_DIRMASK) | IOC_IN: args->cmd = EVIOCGRAB; break; case (EVIOCREVOKE & ~IOC_DIRMASK) | IOC_IN: args->cmd = EVIOCREVOKE; break; case (EVIOCRMFF & ~IOC_DIRMASK) | IOC_IN: args->cmd = EVIOCRMFF; break; case EVIOCSCLOCKID: { error = copyin(PTRIN(args->arg), &clock, sizeof(clock)); if (error != 0) return (error); if (clock & ~(LINUX_IOCTL_EVDEV_CLK)) return (EINVAL); error = linux_to_native_clockid(&clock, clock); if (error != 0) return (error); error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) return (error); error = fo_ioctl(fp, EVIOCSCLOCKID, &clock, td->td_ucred, td); fdrop(fp, td); return (error); } default: break; } if (IOCBASECMD(args->cmd) == ((EVIOCGMTSLOTS(0) & ~IOC_DIRMASK) | IOC_OUT)) args->cmd = (args->cmd & ~IOC_DIRMASK) | IOC_INOUT; return (sys_ioctl(td, (struct ioctl_args *)args)); } /* * main ioctl syscall function */ int linux_ioctl(struct thread *td, struct linux_ioctl_args *args) { struct file *fp; struct linux_ioctl_handler_element *he; int error, cmd; #ifdef DEBUG if (ldebug(ioctl)) printf(ARGS(ioctl, "%d, %04lx, *"), args->fd, (unsigned long)args->cmd); #endif error = fget(td, args->fd, &cap_ioctl_rights, &fp); if (error != 0) return (error); if ((fp->f_flag & (FREAD|FWRITE)) == 0) { fdrop(fp, td); return (EBADF); } /* Iterate over the ioctl handlers */ cmd = args->cmd & 0xffff; sx_slock(&linux_ioctl_sx); mtx_lock(&Giant); #ifdef COMPAT_LINUX32 TAILQ_FOREACH(he, &linux32_ioctl_handlers, list) { if (cmd >= he->low && cmd <= he->high) { error = (*he->func)(td, args); if (error != ENOIOCTL) { mtx_unlock(&Giant); sx_sunlock(&linux_ioctl_sx); fdrop(fp, td); return (error); } } } #endif TAILQ_FOREACH(he, &linux_ioctl_handlers, list) { if (cmd >= he->low && cmd <= he->high) { error = (*he->func)(td, args); if (error != ENOIOCTL) { mtx_unlock(&Giant); sx_sunlock(&linux_ioctl_sx); fdrop(fp, td); return (error); } } } mtx_unlock(&Giant); sx_sunlock(&linux_ioctl_sx); fdrop(fp, td); switch (args->cmd & 0xffff) { case LINUX_BTRFS_IOC_CLONE: return (ENOTSUP); default: linux_msg(td, "ioctl fd=%d, cmd=0x%x ('%c',%d) is not implemented", args->fd, (int)(args->cmd & 0xffff), (int)(args->cmd & 0xff00) >> 8, (int)(args->cmd & 0xff)); break; } return (EINVAL); } int linux_ioctl_register_handler(struct linux_ioctl_handler *h) { struct linux_ioctl_handler_element *he, *cur; if (h == NULL || h->func == NULL) return (EINVAL); /* * Reuse the element if the handler is already on the list, otherwise * create a new element. */ sx_xlock(&linux_ioctl_sx); TAILQ_FOREACH(he, &linux_ioctl_handlers, list) { if (he->func == h->func) break; } if (he == NULL) { he = malloc(sizeof(*he), M_LINUX, M_WAITOK); he->func = h->func; } else TAILQ_REMOVE(&linux_ioctl_handlers, he, list); /* Initialize range information. */ he->low = h->low; he->high = h->high; he->span = h->high - h->low + 1; /* Add the element to the list, sorted on span. */ TAILQ_FOREACH(cur, &linux_ioctl_handlers, list) { if (cur->span > he->span) { TAILQ_INSERT_BEFORE(cur, he, list); sx_xunlock(&linux_ioctl_sx); return (0); } } TAILQ_INSERT_TAIL(&linux_ioctl_handlers, he, list); sx_xunlock(&linux_ioctl_sx); return (0); } int linux_ioctl_unregister_handler(struct linux_ioctl_handler *h) { struct linux_ioctl_handler_element *he; if (h == NULL || h->func == NULL) return (EINVAL); sx_xlock(&linux_ioctl_sx); TAILQ_FOREACH(he, &linux_ioctl_handlers, list) { if (he->func == h->func) { TAILQ_REMOVE(&linux_ioctl_handlers, he, list); sx_xunlock(&linux_ioctl_sx); free(he, M_LINUX); return (0); } } sx_xunlock(&linux_ioctl_sx); return (EINVAL); } #ifdef COMPAT_LINUX32 int linux32_ioctl_register_handler(struct linux_ioctl_handler *h) { struct linux_ioctl_handler_element *he, *cur; if (h == NULL || h->func == NULL) return (EINVAL); /* * Reuse the element if the handler is already on the list, otherwise * create a new element. */ sx_xlock(&linux_ioctl_sx); TAILQ_FOREACH(he, &linux32_ioctl_handlers, list) { if (he->func == h->func) break; } if (he == NULL) { he = malloc(sizeof(*he), M_LINUX, M_WAITOK); he->func = h->func; } else TAILQ_REMOVE(&linux32_ioctl_handlers, he, list); /* Initialize range information. */ he->low = h->low; he->high = h->high; he->span = h->high - h->low + 1; /* Add the element to the list, sorted on span. */ TAILQ_FOREACH(cur, &linux32_ioctl_handlers, list) { if (cur->span > he->span) { TAILQ_INSERT_BEFORE(cur, he, list); sx_xunlock(&linux_ioctl_sx); return (0); } } TAILQ_INSERT_TAIL(&linux32_ioctl_handlers, he, list); sx_xunlock(&linux_ioctl_sx); return (0); } int linux32_ioctl_unregister_handler(struct linux_ioctl_handler *h) { struct linux_ioctl_handler_element *he; if (h == NULL || h->func == NULL) return (EINVAL); sx_xlock(&linux_ioctl_sx); TAILQ_FOREACH(he, &linux32_ioctl_handlers, list) { if (he->func == h->func) { TAILQ_REMOVE(&linux32_ioctl_handlers, he, list); sx_xunlock(&linux_ioctl_sx); free(he, M_LINUX); return (0); } } sx_xunlock(&linux_ioctl_sx); return (EINVAL); } #endif Index: stable/12/sys/compat/linux/linux_timer.h =================================================================== --- stable/12/sys/compat/linux/linux_timer.h (revision 360445) +++ stable/12/sys/compat/linux/linux_timer.h (revision 360446) @@ -1,126 +1,118 @@ /*- * Copyright (c) 2014 Bjoern A. Zeeb * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-11-C-0249 * ("MRC2"), as part of the DARPA MRC research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _LINUX_TIMER_H #define _LINUX_TIMER_H +#include + #ifndef __LINUX_ARCH_SIGEV_PREAMBLE_SIZE #define __LINUX_ARCH_SIGEV_PREAMBLE_SIZE \ (sizeof(l_int) * 2 + sizeof(l_sigval_t)) #endif #define LINUX_SIGEV_MAX_SIZE 64 #define LINUX_SIGEV_PAD_SIZE \ ((LINUX_SIGEV_MAX_SIZE - __LINUX_ARCH_SIGEV_PREAMBLE_SIZE) / \ sizeof(l_int)) #define LINUX_CLOCK_REALTIME 0 #define LINUX_CLOCK_MONOTONIC 1 #define LINUX_CLOCK_PROCESS_CPUTIME_ID 2 #define LINUX_CLOCK_THREAD_CPUTIME_ID 3 #define LINUX_CLOCK_MONOTONIC_RAW 4 #define LINUX_CLOCK_REALTIME_COARSE 5 #define LINUX_CLOCK_MONOTONIC_COARSE 6 #define LINUX_CLOCK_BOOTTIME 7 #define LINUX_CLOCK_REALTIME_ALARM 8 #define LINUX_CLOCK_BOOTTIME_ALARM 9 #define LINUX_CLOCK_SGI_CYCLE 10 #define LINUX_CLOCK_TAI 11 #define LINUX_CPUCLOCK_PERTHREAD_MASK 4 #define LINUX_CPUCLOCK_MASK 3 #define LINUX_CPUCLOCK_WHICH(clock) \ ((clock) & (clockid_t) LINUX_CPUCLOCK_MASK) #define LINUX_CPUCLOCK_PROF 0 #define LINUX_CPUCLOCK_VIRT 1 #define LINUX_CPUCLOCK_SCHED 2 #define LINUX_CPUCLOCK_MAX 3 #define LINUX_CLOCKFD LINUX_CPUCLOCK_MAX #define LINUX_CLOCKFD_MASK \ (LINUX_CPUCLOCK_PERTHREAD_MASK|LINUX_CPUCLOCK_MASK) #define LINUX_CPUCLOCK_ID(clock) ((pid_t) ~((clock) >> 3)) #define LINUX_CPUCLOCK_PERTHREAD(clock) \ (((clock) & (clockid_t) LINUX_CPUCLOCK_PERTHREAD_MASK) != 0) #define LINUX_TIMER_ABSTIME 0x01 #define L_SIGEV_SIGNAL 0 #define L_SIGEV_NONE 1 #define L_SIGEV_THREAD 2 #define L_SIGEV_THREAD_ID 4 - -#define TS_CP(src,dst,fld) do { \ - CP((src).fld,(dst).fld,tv_sec); \ - CP((src).fld,(dst).fld,tv_nsec); \ -} while (0) - -#define ITS_CP(src, dst) do { \ - TS_CP((src), (dst), it_interval); \ - TS_CP((src), (dst), it_value); \ -} while (0) struct l_sigevent { l_sigval_t sigev_value; l_int sigev_signo; l_int sigev_notify; union { l_int _pad[LINUX_SIGEV_PAD_SIZE]; l_int _tid; struct { l_uintptr_t _function; l_uintptr_t _attribute; } _l_sigev_thread; } _l_sigev_un; } #if defined(__amd64__) && defined(COMPAT_LINUX32) __packed #endif ; struct l_itimerspec { struct l_timespec it_interval; struct l_timespec it_value; }; int native_to_linux_timespec(struct l_timespec *, struct timespec *); int linux_to_native_timespec(struct timespec *, struct l_timespec *); int linux_to_native_clockid(clockid_t *, clockid_t); int native_to_linux_itimerspec(struct l_itimerspec *, struct itimerspec *); int linux_to_native_itimerspec(struct itimerspec *, struct l_itimerspec *); int linux_to_native_timerflags(int *, int); #endif /* _LINUX_TIMER_H */ Index: stable/12/sys/dev/ipmi/ipmi.c =================================================================== --- stable/12/sys/dev/ipmi/ipmi.c (revision 360445) +++ stable/12/sys/dev/ipmi/ipmi.c (revision 360446) @@ -1,1104 +1,1103 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 IronPort Systems Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef LOCAL_MODULE #include #include #else #include #include #endif +#ifdef IPMICTL_SEND_COMMAND_32 +#include +#endif + /* * Driver request structures are allocated on the stack via alloca() to * avoid calling malloc(), especially for the watchdog handler. * To avoid too much stack growth, a previously allocated structure can * be reused via IPMI_INIT_DRIVER_REQUEST(), but the caller should ensure * that there is adequate reply/request space in the original allocation. */ #define IPMI_INIT_DRIVER_REQUEST(req, addr, cmd, reqlen, replylen) \ bzero((req), sizeof(struct ipmi_request)); \ ipmi_init_request((req), NULL, 0, (addr), (cmd), (reqlen), (replylen)) #define IPMI_ALLOC_DRIVER_REQUEST(req, addr, cmd, reqlen, replylen) \ (req) = __builtin_alloca(sizeof(struct ipmi_request) + \ (reqlen) + (replylen)); \ IPMI_INIT_DRIVER_REQUEST((req), (addr), (cmd), (reqlen), \ (replylen)) #ifdef IPMB static int ipmi_ipmb_checksum(u_char, int); static int ipmi_ipmb_send_message(device_t, u_char, u_char, u_char, u_char, u_char, int) #endif static d_ioctl_t ipmi_ioctl; static d_poll_t ipmi_poll; static d_open_t ipmi_open; static void ipmi_dtor(void *arg); int ipmi_attached = 0; static int on = 1; static bool wd_in_shutdown = false; static int wd_timer_actions = IPMI_SET_WD_ACTION_POWER_CYCLE; static int wd_shutdown_countdown = 0; /* sec */ static int wd_startup_countdown = 0; /* sec */ static int wd_pretimeout_countdown = 120; /* sec */ static int cycle_wait = 10; /* sec */ static SYSCTL_NODE(_hw, OID_AUTO, ipmi, CTLFLAG_RD, 0, "IPMI driver parameters"); SYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RWTUN, &on, 0, ""); SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_timer_actions, CTLFLAG_RW, &wd_timer_actions, 0, "IPMI watchdog timer actions (including pre-timeout interrupt)"); SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_shutdown_countdown, CTLFLAG_RW, &wd_shutdown_countdown, 0, "IPMI watchdog countdown for shutdown (seconds)"); SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_startup_countdown, CTLFLAG_RDTUN, &wd_startup_countdown, 0, "IPMI watchdog countdown initialized during startup (seconds)"); SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_pretimeout_countdown, CTLFLAG_RW, &wd_pretimeout_countdown, 0, "IPMI watchdog pre-timeout countdown (seconds)"); SYSCTL_INT(_hw_ipmi, OID_AUTO, cyle_wait, CTLFLAG_RWTUN, &cycle_wait, 0, "IPMI power cycle on reboot delay time (seconds)"); static struct cdevsw ipmi_cdevsw = { .d_version = D_VERSION, .d_open = ipmi_open, .d_ioctl = ipmi_ioctl, .d_poll = ipmi_poll, .d_name = "ipmi", }; static MALLOC_DEFINE(M_IPMI, "ipmi", "ipmi"); static int ipmi_open(struct cdev *cdev, int flags, int fmt, struct thread *td) { struct ipmi_device *dev; struct ipmi_softc *sc; int error; if (!on) return (ENOENT); /* Initialize the per file descriptor data. */ dev = malloc(sizeof(struct ipmi_device), M_IPMI, M_WAITOK | M_ZERO); error = devfs_set_cdevpriv(dev, ipmi_dtor); if (error) { free(dev, M_IPMI); return (error); } sc = cdev->si_drv1; TAILQ_INIT(&dev->ipmi_completed_requests); dev->ipmi_address = IPMI_BMC_SLAVE_ADDR; dev->ipmi_lun = IPMI_BMC_SMS_LUN; dev->ipmi_softc = sc; IPMI_LOCK(sc); sc->ipmi_opened++; IPMI_UNLOCK(sc); return (0); } static int ipmi_poll(struct cdev *cdev, int poll_events, struct thread *td) { struct ipmi_device *dev; struct ipmi_softc *sc; int revents = 0; if (devfs_get_cdevpriv((void **)&dev)) return (0); sc = cdev->si_drv1; IPMI_LOCK(sc); if (poll_events & (POLLIN | POLLRDNORM)) { if (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) revents |= poll_events & (POLLIN | POLLRDNORM); if (dev->ipmi_requests == 0) revents |= POLLERR; } if (revents == 0) { if (poll_events & (POLLIN | POLLRDNORM)) selrecord(td, &dev->ipmi_select); } IPMI_UNLOCK(sc); return (revents); } static void ipmi_purge_completed_requests(struct ipmi_device *dev) { struct ipmi_request *req; while (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) { req = TAILQ_FIRST(&dev->ipmi_completed_requests); TAILQ_REMOVE(&dev->ipmi_completed_requests, req, ir_link); dev->ipmi_requests--; ipmi_free_request(req); } } static void ipmi_dtor(void *arg) { struct ipmi_request *req, *nreq; struct ipmi_device *dev; struct ipmi_softc *sc; dev = arg; sc = dev->ipmi_softc; IPMI_LOCK(sc); if (dev->ipmi_requests) { /* Throw away any pending requests for this device. */ TAILQ_FOREACH_SAFE(req, &sc->ipmi_pending_requests, ir_link, nreq) { if (req->ir_owner == dev) { TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); dev->ipmi_requests--; ipmi_free_request(req); } } /* Throw away any pending completed requests for this device. */ ipmi_purge_completed_requests(dev); /* * If we still have outstanding requests, they must be stuck * in an interface driver, so wait for those to drain. */ dev->ipmi_closing = 1; while (dev->ipmi_requests > 0) { msleep(&dev->ipmi_requests, &sc->ipmi_requests_lock, PWAIT, "ipmidrain", 0); ipmi_purge_completed_requests(dev); } } sc->ipmi_opened--; IPMI_UNLOCK(sc); /* Cleanup. */ free(dev, M_IPMI); } #ifdef IPMB static int ipmi_ipmb_checksum(u_char *data, int len) { u_char sum = 0; for (; len; len--) { sum += *data++; } return (-sum); } /* XXX: Needs work */ static int ipmi_ipmb_send_message(device_t dev, u_char channel, u_char netfn, u_char command, u_char seq, u_char *data, int data_len) { struct ipmi_softc *sc = device_get_softc(dev); struct ipmi_request *req; u_char slave_addr = 0x52; int error; IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_SEND_MSG, data_len + 8, 0); req->ir_request[0] = channel; req->ir_request[1] = slave_addr; req->ir_request[2] = IPMI_ADDR(netfn, 0); req->ir_request[3] = ipmi_ipmb_checksum(&req->ir_request[1], 2); req->ir_request[4] = sc->ipmi_address; req->ir_request[5] = IPMI_ADDR(seq, sc->ipmi_lun); req->ir_request[6] = command; bcopy(data, &req->ir_request[7], data_len); temp[data_len + 7] = ipmi_ipmb_checksum(&req->ir_request[4], data_len + 3); ipmi_submit_driver_request(sc, req); error = req->ir_error; return (error); } static int ipmi_handle_attn(struct ipmi_softc *sc) { struct ipmi_request *req; int error; device_printf(sc->ipmi_dev, "BMC has a message\n"); IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG_FLAGS, 0, 1); ipmi_submit_driver_request(sc, req); if (req->ir_error == 0 && req->ir_compcode == 0) { if (req->ir_reply[0] & IPMI_MSG_BUFFER_FULL) { device_printf(sc->ipmi_dev, "message buffer full"); } if (req->ir_reply[0] & IPMI_WDT_PRE_TIMEOUT) { device_printf(sc->ipmi_dev, "watchdog about to go off"); } if (req->ir_reply[0] & IPMI_MSG_AVAILABLE) { IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG, 0, 16); device_printf(sc->ipmi_dev, "throw out message "); dump_buf(temp, 16); } } error = req->ir_error; return (error); } -#endif - -#ifdef IPMICTL_SEND_COMMAND_32 -#define PTRIN(p) ((void *)(uintptr_t)(p)) -#define PTROUT(p) ((uintptr_t)(p)) #endif static int ipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int flags, struct thread *td) { struct ipmi_softc *sc; struct ipmi_device *dev; struct ipmi_request *kreq; struct ipmi_req *req = (struct ipmi_req *)data; struct ipmi_recv *recv = (struct ipmi_recv *)data; struct ipmi_addr addr; #ifdef IPMICTL_SEND_COMMAND_32 struct ipmi_req32 *req32 = (struct ipmi_req32 *)data; struct ipmi_recv32 *recv32 = (struct ipmi_recv32 *)data; union { struct ipmi_req req; struct ipmi_recv recv; } thunk32; #endif int error, len; error = devfs_get_cdevpriv((void **)&dev); if (error) return (error); sc = cdev->si_drv1; #ifdef IPMICTL_SEND_COMMAND_32 /* Convert 32-bit structures to native. */ switch (cmd) { case IPMICTL_SEND_COMMAND_32: req = &thunk32.req; req->addr = PTRIN(req32->addr); req->addr_len = req32->addr_len; req->msgid = req32->msgid; req->msg.netfn = req32->msg.netfn; req->msg.cmd = req32->msg.cmd; req->msg.data_len = req32->msg.data_len; req->msg.data = PTRIN(req32->msg.data); break; case IPMICTL_RECEIVE_MSG_TRUNC_32: case IPMICTL_RECEIVE_MSG_32: recv = &thunk32.recv; recv->addr = PTRIN(recv32->addr); recv->addr_len = recv32->addr_len; recv->msg.data_len = recv32->msg.data_len; recv->msg.data = PTRIN(recv32->msg.data); break; } #endif switch (cmd) { #ifdef IPMICTL_SEND_COMMAND_32 case IPMICTL_SEND_COMMAND_32: #endif case IPMICTL_SEND_COMMAND: /* * XXX: Need to add proper handling of this. */ error = copyin(req->addr, &addr, sizeof(addr)); if (error) return (error); IPMI_LOCK(sc); /* clear out old stuff in queue of stuff done */ /* XXX: This seems odd. */ while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))) { TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link); dev->ipmi_requests--; ipmi_free_request(kreq); } IPMI_UNLOCK(sc); kreq = ipmi_alloc_request(dev, req->msgid, IPMI_ADDR(req->msg.netfn, 0), req->msg.cmd, req->msg.data_len, IPMI_MAX_RX); error = copyin(req->msg.data, kreq->ir_request, req->msg.data_len); if (error) { ipmi_free_request(kreq); return (error); } IPMI_LOCK(sc); dev->ipmi_requests++; error = sc->ipmi_enqueue_request(sc, kreq); IPMI_UNLOCK(sc); if (error) return (error); break; #ifdef IPMICTL_SEND_COMMAND_32 case IPMICTL_RECEIVE_MSG_TRUNC_32: case IPMICTL_RECEIVE_MSG_32: #endif case IPMICTL_RECEIVE_MSG_TRUNC: case IPMICTL_RECEIVE_MSG: error = copyin(recv->addr, &addr, sizeof(addr)); if (error) return (error); IPMI_LOCK(sc); kreq = TAILQ_FIRST(&dev->ipmi_completed_requests); if (kreq == NULL) { IPMI_UNLOCK(sc); return (EAGAIN); } addr.channel = IPMI_BMC_CHANNEL; /* XXX */ recv->recv_type = IPMI_RESPONSE_RECV_TYPE; recv->msgid = kreq->ir_msgid; recv->msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2; recv->msg.cmd = kreq->ir_command; error = kreq->ir_error; if (error) { TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link); dev->ipmi_requests--; IPMI_UNLOCK(sc); ipmi_free_request(kreq); return (error); } len = kreq->ir_replylen + 1; if (recv->msg.data_len < len && (cmd == IPMICTL_RECEIVE_MSG #ifdef IPMICTL_RECEIVE_MSG_32 || cmd == IPMICTL_RECEIVE_MSG_32 #endif )) { IPMI_UNLOCK(sc); return (EMSGSIZE); } TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link); dev->ipmi_requests--; IPMI_UNLOCK(sc); len = min(recv->msg.data_len, len); recv->msg.data_len = len; error = copyout(&addr, recv->addr,sizeof(addr)); if (error == 0) error = copyout(&kreq->ir_compcode, recv->msg.data, 1); if (error == 0) error = copyout(kreq->ir_reply, recv->msg.data + 1, len - 1); ipmi_free_request(kreq); if (error) return (error); break; case IPMICTL_SET_MY_ADDRESS_CMD: IPMI_LOCK(sc); dev->ipmi_address = *(int*)data; IPMI_UNLOCK(sc); break; case IPMICTL_GET_MY_ADDRESS_CMD: IPMI_LOCK(sc); *(int*)data = dev->ipmi_address; IPMI_UNLOCK(sc); break; case IPMICTL_SET_MY_LUN_CMD: IPMI_LOCK(sc); dev->ipmi_lun = *(int*)data & 0x3; IPMI_UNLOCK(sc); break; case IPMICTL_GET_MY_LUN_CMD: IPMI_LOCK(sc); *(int*)data = dev->ipmi_lun; IPMI_UNLOCK(sc); break; case IPMICTL_SET_GETS_EVENTS_CMD: /* device_printf(sc->ipmi_dev, "IPMICTL_SET_GETS_EVENTS_CMD NA\n"); */ break; case IPMICTL_REGISTER_FOR_CMD: case IPMICTL_UNREGISTER_FOR_CMD: return (EOPNOTSUPP); default: device_printf(sc->ipmi_dev, "Unknown IOCTL %lX\n", cmd); return (ENOIOCTL); } #ifdef IPMICTL_SEND_COMMAND_32 /* Update changed fields in 32-bit structures. */ switch (cmd) { case IPMICTL_RECEIVE_MSG_TRUNC_32: case IPMICTL_RECEIVE_MSG_32: recv32->recv_type = recv->recv_type; recv32->msgid = recv->msgid; recv32->msg.netfn = recv->msg.netfn; recv32->msg.cmd = recv->msg.cmd; recv32->msg.data_len = recv->msg.data_len; break; } #endif return (0); } /* * Request management. */ static __inline void ipmi_init_request(struct ipmi_request *req, struct ipmi_device *dev, long msgid, uint8_t addr, uint8_t command, size_t requestlen, size_t replylen) { req->ir_owner = dev; req->ir_msgid = msgid; req->ir_addr = addr; req->ir_command = command; if (requestlen) { req->ir_request = (char *)&req[1]; req->ir_requestlen = requestlen; } if (replylen) { req->ir_reply = (char *)&req[1] + requestlen; req->ir_replybuflen = replylen; } } /* Allocate a new request with request and reply buffers. */ struct ipmi_request * ipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr, uint8_t command, size_t requestlen, size_t replylen) { struct ipmi_request *req; req = malloc(sizeof(struct ipmi_request) + requestlen + replylen, M_IPMI, M_WAITOK | M_ZERO); ipmi_init_request(req, dev, msgid, addr, command, requestlen, replylen); return (req); } /* Free a request no longer in use. */ void ipmi_free_request(struct ipmi_request *req) { free(req, M_IPMI); } /* Store a processed request on the appropriate completion queue. */ void ipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req) { struct ipmi_device *dev; IPMI_LOCK_ASSERT(sc); /* * Anonymous requests (from inside the driver) always have a * waiter that we awaken. */ if (req->ir_owner == NULL) wakeup(req); else { dev = req->ir_owner; TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link); selwakeup(&dev->ipmi_select); if (dev->ipmi_closing) wakeup(&dev->ipmi_requests); } } /* Perform an internal driver request. */ int ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, int timo) { return (sc->ipmi_driver_request(sc, req, timo)); } /* * Helper routine for polled system interfaces that use * ipmi_polled_enqueue_request() to queue requests. This request * waits until there is a pending request and then returns the first * request. If the driver is shutting down, it returns NULL. */ struct ipmi_request * ipmi_dequeue_request(struct ipmi_softc *sc) { struct ipmi_request *req; IPMI_LOCK_ASSERT(sc); while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests)) cv_wait(&sc->ipmi_request_added, &sc->ipmi_requests_lock); if (sc->ipmi_detaching) return (NULL); req = TAILQ_FIRST(&sc->ipmi_pending_requests); TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); return (req); } /* Default implementation of ipmi_enqueue_request() for polled interfaces. */ int ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req) { IPMI_LOCK_ASSERT(sc); TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link); cv_signal(&sc->ipmi_request_added); return (0); } /* * Watchdog event handler. */ static int ipmi_reset_watchdog(struct ipmi_softc *sc) { struct ipmi_request *req; int error; IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_RESET_WDOG, 0, 0); error = ipmi_submit_driver_request(sc, req, 0); if (error) device_printf(sc->ipmi_dev, "Failed to reset watchdog\n"); return (error); } static int ipmi_set_watchdog(struct ipmi_softc *sc, unsigned int sec) { struct ipmi_request *req; int error; if (sec > 0xffff / 10) return (EINVAL); IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_SET_WDOG, 6, 0); if (sec) { req->ir_request[0] = IPMI_SET_WD_TIMER_DONT_STOP | IPMI_SET_WD_TIMER_SMS_OS; req->ir_request[1] = (wd_timer_actions & 0xff); req->ir_request[2] = (wd_pretimeout_countdown & 0xff); req->ir_request[3] = 0; /* Timer use */ req->ir_request[4] = (sec * 10) & 0xff; req->ir_request[5] = (sec * 10) >> 8; } else { req->ir_request[0] = IPMI_SET_WD_TIMER_SMS_OS; req->ir_request[1] = 0; req->ir_request[2] = 0; req->ir_request[3] = 0; /* Timer use */ req->ir_request[4] = 0; req->ir_request[5] = 0; } error = ipmi_submit_driver_request(sc, req, 0); if (error) device_printf(sc->ipmi_dev, "Failed to set watchdog\n"); return (error); } static void ipmi_wd_event(void *arg, unsigned int cmd, int *error) { struct ipmi_softc *sc = arg; unsigned int timeout; int e; /* Ignore requests while disabled. */ if (!on) return; /* * To prevent infinite hangs, we don't let anyone pat or change * the watchdog when we're shutting down. (See ipmi_shutdown_event().) * However, we do want to keep patting the watchdog while we are doing * a coredump. */ if (wd_in_shutdown) { if (dumping && sc->ipmi_watchdog_active) ipmi_reset_watchdog(sc); return; } cmd &= WD_INTERVAL; if (cmd > 0 && cmd <= 63) { timeout = ((uint64_t)1 << cmd) / 1000000000; if (timeout == 0) timeout = 1; if (timeout != sc->ipmi_watchdog_active || wd_timer_actions != sc->ipmi_watchdog_actions || wd_pretimeout_countdown != sc->ipmi_watchdog_pretimeout) { e = ipmi_set_watchdog(sc, timeout); if (e == 0) { sc->ipmi_watchdog_active = timeout; sc->ipmi_watchdog_actions = wd_timer_actions; sc->ipmi_watchdog_pretimeout = wd_pretimeout_countdown; } else { (void)ipmi_set_watchdog(sc, 0); sc->ipmi_watchdog_active = 0; sc->ipmi_watchdog_actions = 0; sc->ipmi_watchdog_pretimeout = 0; } } if (sc->ipmi_watchdog_active != 0) { e = ipmi_reset_watchdog(sc); if (e == 0) { *error = 0; } else { (void)ipmi_set_watchdog(sc, 0); sc->ipmi_watchdog_active = 0; sc->ipmi_watchdog_actions = 0; sc->ipmi_watchdog_pretimeout = 0; } } } else if (atomic_readandclear_int(&sc->ipmi_watchdog_active) != 0) { sc->ipmi_watchdog_actions = 0; sc->ipmi_watchdog_pretimeout = 0; e = ipmi_set_watchdog(sc, 0); if (e != 0 && cmd == 0) *error = EOPNOTSUPP; } } static void ipmi_shutdown_event(void *arg, unsigned int cmd, int *error) { struct ipmi_softc *sc = arg; /* Ignore event if disabled. */ if (!on) return; /* * Positive wd_shutdown_countdown value will re-arm watchdog; * Zero value in wd_shutdown_countdown will disable watchdog; * Negative value in wd_shutdown_countdown will keep existing state; * * Revert to using a power cycle to ensure that the watchdog will * do something useful here. Having the watchdog send an NMI * instead is useless during shutdown, and might be ignored if an * NMI already triggered. */ wd_in_shutdown = true; if (wd_shutdown_countdown == 0) { /* disable watchdog */ ipmi_set_watchdog(sc, 0); sc->ipmi_watchdog_active = 0; } else if (wd_shutdown_countdown > 0) { /* set desired action and time, and, reset watchdog */ wd_timer_actions = IPMI_SET_WD_ACTION_POWER_CYCLE; ipmi_set_watchdog(sc, wd_shutdown_countdown); sc->ipmi_watchdog_active = wd_shutdown_countdown; ipmi_reset_watchdog(sc); } } static void ipmi_power_cycle(void *arg, int howto) { struct ipmi_softc *sc = arg; struct ipmi_request *req; /* * Ignore everything except power cycling requests */ if ((howto & RB_POWERCYCLE) == 0) return; device_printf(sc->ipmi_dev, "Power cycling using IPMI\n"); /* * Send a CHASSIS_CONTROL command to the CHASSIS device, subcommand 2 * as described in IPMI v2.0 spec section 28.3. */ IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_CHASSIS_REQUEST, 0), IPMI_CHASSIS_CONTROL, 1, 0); req->ir_request[0] = IPMI_CC_POWER_CYCLE; ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); if (req->ir_error != 0 || req->ir_compcode != 0) { device_printf(sc->ipmi_dev, "Power cycling via IPMI failed code %#x %#x\n", req->ir_error, req->ir_compcode); return; } /* * BMCs are notoriously slow, give it cyle_wait seconds for the power * down leg of the power cycle. If that fails, fallback to the next * hanlder in the shutdown_final chain and/or the platform failsafe. */ DELAY(cycle_wait * 1000 * 1000); device_printf(sc->ipmi_dev, "Power cycling via IPMI timed out\n"); } static void ipmi_startup(void *arg) { struct ipmi_softc *sc = arg; struct ipmi_request *req; device_t dev; int error, i; config_intrhook_disestablish(&sc->ipmi_ich); dev = sc->ipmi_dev; /* Initialize interface-independent state. */ mtx_init(&sc->ipmi_requests_lock, "ipmi requests", NULL, MTX_DEF); mtx_init(&sc->ipmi_io_lock, "ipmi io", NULL, MTX_DEF); cv_init(&sc->ipmi_request_added, "ipmireq"); TAILQ_INIT(&sc->ipmi_pending_requests); /* Initialize interface-dependent state. */ error = sc->ipmi_startup(sc); if (error) { device_printf(dev, "Failed to initialize interface: %d\n", error); return; } /* Send a GET_DEVICE_ID request. */ IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_DEVICE_ID, 0, 15); error = ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); if (error == EWOULDBLOCK) { device_printf(dev, "Timed out waiting for GET_DEVICE_ID\n"); return; } else if (error) { device_printf(dev, "Failed GET_DEVICE_ID: %d\n", error); return; } else if (req->ir_compcode != 0) { device_printf(dev, "Bad completion code for GET_DEVICE_ID: %d\n", req->ir_compcode); return; } else if (req->ir_replylen < 5) { device_printf(dev, "Short reply for GET_DEVICE_ID: %d\n", req->ir_replylen); return; } device_printf(dev, "IPMI device rev. %d, firmware rev. %d.%d%d, " "version %d.%d, device support mask %#x\n", req->ir_reply[1] & 0x0f, req->ir_reply[2] & 0x7f, req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f, req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4, req->ir_reply[5]); sc->ipmi_dev_support = req->ir_reply[5]; IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_CLEAR_FLAGS, 1, 0); ipmi_submit_driver_request(sc, req, 0); /* XXX: Magic numbers */ if (req->ir_compcode == 0xc0) { device_printf(dev, "Clear flags is busy\n"); } if (req->ir_compcode == 0xc1) { device_printf(dev, "Clear flags illegal\n"); } for (i = 0; i < 8; i++) { IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_CHANNEL_INFO, 1, 0); req->ir_request[0] = i; ipmi_submit_driver_request(sc, req, 0); if (req->ir_compcode != 0) break; } device_printf(dev, "Number of channels %d\n", i); /* * Probe for watchdog, but only for backends which support * polled driver requests. */ if (sc->ipmi_driver_requests_polled) { IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_WDOG, 0, 0); ipmi_submit_driver_request(sc, req, 0); if (req->ir_compcode == 0x00) { device_printf(dev, "Attached watchdog\n"); /* register the watchdog event handler */ sc->ipmi_watchdog_tag = EVENTHANDLER_REGISTER( watchdog_list, ipmi_wd_event, sc, 0); sc->ipmi_shutdown_tag = EVENTHANDLER_REGISTER( shutdown_pre_sync, ipmi_shutdown_event, sc, 0); } } sc->ipmi_cdev = make_dev(&ipmi_cdevsw, device_get_unit(dev), UID_ROOT, GID_OPERATOR, 0660, "ipmi%d", device_get_unit(dev)); if (sc->ipmi_cdev == NULL) { device_printf(dev, "Failed to create cdev\n"); return; } sc->ipmi_cdev->si_drv1 = sc; /* * Set initial watchdog state. If desired, set an initial * watchdog on startup. Or, if the watchdog device is * disabled, clear any existing watchdog. */ if (on && wd_startup_countdown > 0) { wd_timer_actions = IPMI_SET_WD_ACTION_POWER_CYCLE; if (ipmi_set_watchdog(sc, wd_startup_countdown) == 0 && ipmi_reset_watchdog(sc) == 0) { sc->ipmi_watchdog_active = wd_startup_countdown; sc->ipmi_watchdog_actions = wd_timer_actions; sc->ipmi_watchdog_pretimeout = wd_pretimeout_countdown; } else (void)ipmi_set_watchdog(sc, 0); ipmi_reset_watchdog(sc); } else if (!on) (void)ipmi_set_watchdog(sc, 0); /* * Power cycle the system off using IPMI. We use last - 1 since we don't * handle all the other kinds of reboots. We'll let others handle them. * We only try to do this if the BMC supports the Chassis device. */ if (sc->ipmi_dev_support & IPMI_ADS_CHASSIS) { device_printf(dev, "Establishing power cycle handler\n"); sc->ipmi_power_cycle_tag = EVENTHANDLER_REGISTER(shutdown_final, ipmi_power_cycle, sc, SHUTDOWN_PRI_LAST - 1); } } int ipmi_attach(device_t dev) { struct ipmi_softc *sc = device_get_softc(dev); int error; if (sc->ipmi_irq_res != NULL && sc->ipmi_intr != NULL) { error = bus_setup_intr(dev, sc->ipmi_irq_res, INTR_TYPE_MISC, NULL, sc->ipmi_intr, sc, &sc->ipmi_irq); if (error) { device_printf(dev, "can't set up interrupt\n"); return (error); } } bzero(&sc->ipmi_ich, sizeof(struct intr_config_hook)); sc->ipmi_ich.ich_func = ipmi_startup; sc->ipmi_ich.ich_arg = sc; if (config_intrhook_establish(&sc->ipmi_ich) != 0) { device_printf(dev, "can't establish configuration hook\n"); return (ENOMEM); } ipmi_attached = 1; return (0); } int ipmi_detach(device_t dev) { struct ipmi_softc *sc; sc = device_get_softc(dev); /* Fail if there are any open handles. */ IPMI_LOCK(sc); if (sc->ipmi_opened) { IPMI_UNLOCK(sc); return (EBUSY); } IPMI_UNLOCK(sc); if (sc->ipmi_cdev) destroy_dev(sc->ipmi_cdev); /* Detach from watchdog handling and turn off watchdog. */ if (sc->ipmi_shutdown_tag) EVENTHANDLER_DEREGISTER(shutdown_pre_sync, sc->ipmi_shutdown_tag); if (sc->ipmi_watchdog_tag) { EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_watchdog_tag); ipmi_set_watchdog(sc, 0); } /* Detach from shutdown handling for power cycle reboot */ if (sc->ipmi_power_cycle_tag) EVENTHANDLER_DEREGISTER(shutdown_final, sc->ipmi_power_cycle_tag); /* XXX: should use shutdown callout I think. */ /* If the backend uses a kthread, shut it down. */ IPMI_LOCK(sc); sc->ipmi_detaching = 1; if (sc->ipmi_kthread) { cv_broadcast(&sc->ipmi_request_added); msleep(sc->ipmi_kthread, &sc->ipmi_requests_lock, 0, "ipmi_wait", 0); } IPMI_UNLOCK(sc); if (sc->ipmi_irq) bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); ipmi_release_resources(dev); mtx_destroy(&sc->ipmi_io_lock); mtx_destroy(&sc->ipmi_requests_lock); return (0); } void ipmi_release_resources(device_t dev) { struct ipmi_softc *sc; int i; sc = device_get_softc(dev); if (sc->ipmi_irq) bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); if (sc->ipmi_irq_res) bus_release_resource(dev, SYS_RES_IRQ, sc->ipmi_irq_rid, sc->ipmi_irq_res); for (i = 0; i < MAX_RES; i++) if (sc->ipmi_io_res[i]) bus_release_resource(dev, sc->ipmi_io_type, sc->ipmi_io_rid + i, sc->ipmi_io_res[i]); } devclass_t ipmi_devclass; /* XXX: Why? */ static void ipmi_unload(void *arg) { device_t * devs; int count; int i; if (ipmi_devclass == NULL) return; if (devclass_get_devices(ipmi_devclass, &devs, &count) != 0) return; for (i = 0; i < count; i++) device_delete_child(device_get_parent(devs[i]), devs[i]); free(devs, M_TEMP); } SYSUNINIT(ipmi_unload, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipmi_unload, NULL); #ifdef IMPI_DEBUG static void dump_buf(u_char *data, int len) { char buf[20]; char line[1024]; char temp[30]; int count = 0; int i=0; printf("Address %p len %d\n", data, len); if (len > 256) len = 256; line[0] = '\000'; for (; len > 0; len--, data++) { sprintf(temp, "%02x ", *data); strcat(line, temp); if (*data >= ' ' && *data <= '~') buf[count] = *data; else if (*data >= 'A' && *data <= 'Z') buf[count] = *data; else buf[count] = '.'; if (++count == 16) { buf[count] = '\000'; count = 0; printf(" %3x %s %s\n", i, line, buf); i+=16; line[0] = '\000'; } } buf[count] = '\000'; for (; count != 16; count++) { strcat(line, " "); } printf(" %3x %s %s\n", i, line, buf); } #endif Index: stable/12/sys/dev/mpr/mpr_user.c =================================================================== --- stable/12/sys/dev/mpr/mpr_user.c (revision 360445) +++ stable/12/sys/dev/mpr/mpr_user.c (revision 360446) @@ -1,2627 +1,2618 @@ /*- * Copyright (c) 2008 Yahoo!, Inc. * All rights reserved. * Written by: John Baldwin * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Broadcom Inc. (LSI) MPT-Fusion Host Adapter FreeBSD userland interface */ /*- * Copyright (c) 2011-2015 LSI Corp. * Copyright (c) 2013-2016 Avago Technologies * Copyright 2000-2020 Broadcom Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Broadcom Inc. (LSI) MPT-Fusion Host Adapter FreeBSD * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); /* TODO Move headers to mprvar */ #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static d_open_t mpr_open; static d_close_t mpr_close; static d_ioctl_t mpr_ioctl_devsw; static struct cdevsw mpr_cdevsw = { .d_version = D_VERSION, .d_flags = 0, .d_open = mpr_open, .d_close = mpr_close, .d_ioctl = mpr_ioctl_devsw, .d_name = "mpr", }; typedef int (mpr_user_f)(struct mpr_command *, struct mpr_usr_command *); static mpr_user_f mpi_pre_ioc_facts; static mpr_user_f mpi_pre_port_facts; static mpr_user_f mpi_pre_fw_download; static mpr_user_f mpi_pre_fw_upload; static mpr_user_f mpi_pre_sata_passthrough; static mpr_user_f mpi_pre_smp_passthrough; static mpr_user_f mpi_pre_config; static mpr_user_f mpi_pre_sas_io_unit_control; static int mpr_user_read_cfg_header(struct mpr_softc *, struct mpr_cfg_page_req *); static int mpr_user_read_cfg_page(struct mpr_softc *, struct mpr_cfg_page_req *, void *); static int mpr_user_read_extcfg_header(struct mpr_softc *, struct mpr_ext_cfg_page_req *); static int mpr_user_read_extcfg_page(struct mpr_softc *, struct mpr_ext_cfg_page_req *, void *); static int mpr_user_write_cfg_page(struct mpr_softc *, struct mpr_cfg_page_req *, void *); static int mpr_user_setup_request(struct mpr_command *, struct mpr_usr_command *); static int mpr_user_command(struct mpr_softc *, struct mpr_usr_command *); static int mpr_user_pass_thru(struct mpr_softc *sc, mpr_pass_thru_t *data); static void mpr_user_get_adapter_data(struct mpr_softc *sc, mpr_adapter_data_t *data); static void mpr_user_read_pci_info(struct mpr_softc *sc, mpr_pci_info_t *data); static uint8_t mpr_get_fw_diag_buffer_number(struct mpr_softc *sc, uint32_t unique_id); static int mpr_post_fw_diag_buffer(struct mpr_softc *sc, mpr_fw_diagnostic_buffer_t *pBuffer, uint32_t *return_code); static int mpr_release_fw_diag_buffer(struct mpr_softc *sc, mpr_fw_diagnostic_buffer_t *pBuffer, uint32_t *return_code, uint32_t diag_type); static int mpr_diag_register(struct mpr_softc *sc, mpr_fw_diag_register_t *diag_register, uint32_t *return_code); static int mpr_diag_unregister(struct mpr_softc *sc, mpr_fw_diag_unregister_t *diag_unregister, uint32_t *return_code); static int mpr_diag_query(struct mpr_softc *sc, mpr_fw_diag_query_t *diag_query, uint32_t *return_code); static int mpr_diag_read_buffer(struct mpr_softc *sc, mpr_diag_read_buffer_t *diag_read_buffer, uint8_t *ioctl_buf, uint32_t *return_code); static int mpr_diag_release(struct mpr_softc *sc, mpr_fw_diag_release_t *diag_release, uint32_t *return_code); static int mpr_do_diag_action(struct mpr_softc *sc, uint32_t action, uint8_t *diag_action, uint32_t length, uint32_t *return_code); static int mpr_user_diag_action(struct mpr_softc *sc, mpr_diag_action_t *data); static void mpr_user_event_query(struct mpr_softc *sc, mpr_event_query_t *data); static void mpr_user_event_enable(struct mpr_softc *sc, mpr_event_enable_t *data); static int mpr_user_event_report(struct mpr_softc *sc, mpr_event_report_t *data); static int mpr_user_reg_access(struct mpr_softc *sc, mpr_reg_access_t *data); static int mpr_user_btdh(struct mpr_softc *sc, mpr_btdh_mapping_t *data); static MALLOC_DEFINE(M_MPRUSER, "mpr_user", "Buffers for mpr(4) ioctls"); - -/* Macros from compat/freebsd32/freebsd32.h */ -#define PTRIN(v) (void *)(uintptr_t)(v) -#define PTROUT(v) (uint32_t)(uintptr_t)(v) - -#define CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0) -#define PTRIN_CP(src,dst,fld) \ - do { (dst).fld = PTRIN((src).fld); } while (0) -#define PTROUT_CP(src,dst,fld) \ - do { (dst).fld = PTROUT((src).fld); } while (0) /* * MPI functions that support IEEE SGLs for SAS3. */ static uint8_t ieee_sgl_func_list[] = { MPI2_FUNCTION_SCSI_IO_REQUEST, MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH, MPI2_FUNCTION_SMP_PASSTHROUGH, MPI2_FUNCTION_SATA_PASSTHROUGH, MPI2_FUNCTION_FW_UPLOAD, MPI2_FUNCTION_FW_DOWNLOAD, MPI2_FUNCTION_TARGET_ASSIST, MPI2_FUNCTION_TARGET_STATUS_SEND, MPI2_FUNCTION_TOOLBOX }; int mpr_attach_user(struct mpr_softc *sc) { int unit; unit = device_get_unit(sc->mpr_dev); sc->mpr_cdev = make_dev(&mpr_cdevsw, unit, UID_ROOT, GID_OPERATOR, 0640, "mpr%d", unit); if (sc->mpr_cdev == NULL) return (ENOMEM); sc->mpr_cdev->si_drv1 = sc; return (0); } void mpr_detach_user(struct mpr_softc *sc) { /* XXX: do a purge of pending requests? */ if (sc->mpr_cdev != NULL) destroy_dev(sc->mpr_cdev); } static int mpr_open(struct cdev *dev, int flags, int fmt, struct thread *td) { return (0); } static int mpr_close(struct cdev *dev, int flags, int fmt, struct thread *td) { return (0); } static int mpr_user_read_cfg_header(struct mpr_softc *sc, struct mpr_cfg_page_req *page_req) { MPI2_CONFIG_PAGE_HEADER *hdr; struct mpr_config_params params; int error; hdr = ¶ms.hdr.Struct; params.action = MPI2_CONFIG_ACTION_PAGE_HEADER; params.page_address = le32toh(page_req->page_address); hdr->PageVersion = 0; hdr->PageLength = 0; hdr->PageNumber = page_req->header.PageNumber; hdr->PageType = page_req->header.PageType; params.buffer = NULL; params.length = 0; params.callback = NULL; if ((error = mpr_read_config_page(sc, ¶ms)) != 0) { /* * Leave the request. Without resetting the chip, it's * still owned by it and we'll just get into trouble * freeing it now. Mark it as abandoned so that if it * shows up later it can be freed. */ mpr_printf(sc, "read_cfg_header timed out\n"); return (ETIMEDOUT); } page_req->ioc_status = htole16(params.status); if ((page_req->ioc_status & MPI2_IOCSTATUS_MASK) == MPI2_IOCSTATUS_SUCCESS) { bcopy(hdr, &page_req->header, sizeof(page_req->header)); } return (0); } static int mpr_user_read_cfg_page(struct mpr_softc *sc, struct mpr_cfg_page_req *page_req, void *buf) { MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr; struct mpr_config_params params; int error; reqhdr = buf; hdr = ¶ms.hdr.Struct; hdr->PageVersion = reqhdr->PageVersion; hdr->PageLength = reqhdr->PageLength; hdr->PageNumber = reqhdr->PageNumber; hdr->PageType = reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK; params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; params.page_address = le32toh(page_req->page_address); params.buffer = buf; params.length = le32toh(page_req->len); params.callback = NULL; if ((error = mpr_read_config_page(sc, ¶ms)) != 0) { mpr_printf(sc, "mpr_user_read_cfg_page timed out\n"); return (ETIMEDOUT); } page_req->ioc_status = htole16(params.status); return (0); } static int mpr_user_read_extcfg_header(struct mpr_softc *sc, struct mpr_ext_cfg_page_req *ext_page_req) { MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr; struct mpr_config_params params; int error; hdr = ¶ms.hdr.Ext; params.action = MPI2_CONFIG_ACTION_PAGE_HEADER; hdr->PageVersion = ext_page_req->header.PageVersion; hdr->PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; hdr->ExtPageLength = 0; hdr->PageNumber = ext_page_req->header.PageNumber; hdr->ExtPageType = ext_page_req->header.ExtPageType; params.page_address = le32toh(ext_page_req->page_address); params.buffer = NULL; params.length = 0; params.callback = NULL; if ((error = mpr_read_config_page(sc, ¶ms)) != 0) { /* * Leave the request. Without resetting the chip, it's * still owned by it and we'll just get into trouble * freeing it now. Mark it as abandoned so that if it * shows up later it can be freed. */ mpr_printf(sc, "mpr_user_read_extcfg_header timed out\n"); return (ETIMEDOUT); } ext_page_req->ioc_status = htole16(params.status); if ((ext_page_req->ioc_status & MPI2_IOCSTATUS_MASK) == MPI2_IOCSTATUS_SUCCESS) { ext_page_req->header.PageVersion = hdr->PageVersion; ext_page_req->header.PageNumber = hdr->PageNumber; ext_page_req->header.PageType = hdr->PageType; ext_page_req->header.ExtPageLength = hdr->ExtPageLength; ext_page_req->header.ExtPageType = hdr->ExtPageType; } return (0); } static int mpr_user_read_extcfg_page(struct mpr_softc *sc, struct mpr_ext_cfg_page_req *ext_page_req, void *buf) { MPI2_CONFIG_EXTENDED_PAGE_HEADER *reqhdr, *hdr; struct mpr_config_params params; int error; reqhdr = buf; hdr = ¶ms.hdr.Ext; params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; params.page_address = le32toh(ext_page_req->page_address); hdr->PageVersion = reqhdr->PageVersion; hdr->PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; hdr->PageNumber = reqhdr->PageNumber; hdr->ExtPageType = reqhdr->ExtPageType; hdr->ExtPageLength = reqhdr->ExtPageLength; params.buffer = buf; params.length = le32toh(ext_page_req->len); params.callback = NULL; if ((error = mpr_read_config_page(sc, ¶ms)) != 0) { mpr_printf(sc, "mpr_user_read_extcfg_page timed out\n"); return (ETIMEDOUT); } ext_page_req->ioc_status = htole16(params.status); return (0); } static int mpr_user_write_cfg_page(struct mpr_softc *sc, struct mpr_cfg_page_req *page_req, void *buf) { MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr; struct mpr_config_params params; u_int hdr_attr; int error; reqhdr = buf; hdr = ¶ms.hdr.Struct; hdr_attr = reqhdr->PageType & MPI2_CONFIG_PAGEATTR_MASK; if (hdr_attr != MPI2_CONFIG_PAGEATTR_CHANGEABLE && hdr_attr != MPI2_CONFIG_PAGEATTR_PERSISTENT) { mpr_printf(sc, "page type 0x%x not changeable\n", reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK); return (EINVAL); } /* * There isn't any point in restoring stripped out attributes * if you then mask them going down to issue the request. */ hdr->PageVersion = reqhdr->PageVersion; hdr->PageLength = reqhdr->PageLength; hdr->PageNumber = reqhdr->PageNumber; hdr->PageType = reqhdr->PageType; params.action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; params.page_address = le32toh(page_req->page_address); params.buffer = buf; params.length = le32toh(page_req->len); params.callback = NULL; if ((error = mpr_write_config_page(sc, ¶ms)) != 0) { mpr_printf(sc, "mpr_write_cfg_page timed out\n"); return (ETIMEDOUT); } page_req->ioc_status = htole16(params.status); return (0); } void mpr_init_sge(struct mpr_command *cm, void *req, void *sge) { int off, space; space = (int)cm->cm_sc->reqframesz; off = (uintptr_t)sge - (uintptr_t)req; KASSERT(off < space, ("bad pointers %p %p, off %d, space %d", req, sge, off, space)); cm->cm_sge = sge; cm->cm_sglsize = space - off; } /* * Prepare the mpr_command for an IOC_FACTS request. */ static int mpi_pre_ioc_facts(struct mpr_command *cm, struct mpr_usr_command *cmd) { MPI2_IOC_FACTS_REQUEST *req = (void *)cm->cm_req; MPI2_IOC_FACTS_REPLY *rpl; if (cmd->req_len != sizeof *req) return (EINVAL); if (cmd->rpl_len != sizeof *rpl) return (EINVAL); cm->cm_sge = NULL; cm->cm_sglsize = 0; return (0); } /* * Prepare the mpr_command for a PORT_FACTS request. */ static int mpi_pre_port_facts(struct mpr_command *cm, struct mpr_usr_command *cmd) { MPI2_PORT_FACTS_REQUEST *req = (void *)cm->cm_req; MPI2_PORT_FACTS_REPLY *rpl; if (cmd->req_len != sizeof *req) return (EINVAL); if (cmd->rpl_len != sizeof *rpl) return (EINVAL); cm->cm_sge = NULL; cm->cm_sglsize = 0; return (0); } /* * Prepare the mpr_command for a FW_DOWNLOAD request. */ static int mpi_pre_fw_download(struct mpr_command *cm, struct mpr_usr_command *cmd) { MPI25_FW_DOWNLOAD_REQUEST *req = (void *)cm->cm_req; MPI2_FW_DOWNLOAD_REPLY *rpl; int error; if (cmd->req_len != sizeof *req) return (EINVAL); if (cmd->rpl_len != sizeof *rpl) return (EINVAL); if (cmd->len == 0) return (EINVAL); error = copyin(cmd->buf, cm->cm_data, cmd->len); if (error != 0) return (error); mpr_init_sge(cm, req, &req->SGL); /* * For now, the F/W image must be provided in a single request. */ if ((req->MsgFlags & MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT) == 0) return (EINVAL); if (req->TotalImageSize != cmd->len) return (EINVAL); req->ImageOffset = 0; req->ImageSize = cmd->len; cm->cm_flags |= MPR_CM_FLAGS_DATAOUT; return (mpr_push_ieee_sge(cm, &req->SGL, 0)); } /* * Prepare the mpr_command for a FW_UPLOAD request. */ static int mpi_pre_fw_upload(struct mpr_command *cm, struct mpr_usr_command *cmd) { MPI25_FW_UPLOAD_REQUEST *req = (void *)cm->cm_req; MPI2_FW_UPLOAD_REPLY *rpl; if (cmd->req_len != sizeof *req) return (EINVAL); if (cmd->rpl_len != sizeof *rpl) return (EINVAL); mpr_init_sge(cm, req, &req->SGL); if (cmd->len == 0) { /* Perhaps just asking what the size of the fw is? */ return (0); } req->ImageOffset = 0; req->ImageSize = cmd->len; cm->cm_flags |= MPR_CM_FLAGS_DATAIN; return (mpr_push_ieee_sge(cm, &req->SGL, 0)); } /* * Prepare the mpr_command for a SATA_PASSTHROUGH request. */ static int mpi_pre_sata_passthrough(struct mpr_command *cm, struct mpr_usr_command *cmd) { MPI2_SATA_PASSTHROUGH_REQUEST *req = (void *)cm->cm_req; MPI2_SATA_PASSTHROUGH_REPLY *rpl; if (cmd->req_len != sizeof *req) return (EINVAL); if (cmd->rpl_len != sizeof *rpl) return (EINVAL); mpr_init_sge(cm, req, &req->SGL); return (0); } /* * Prepare the mpr_command for a SMP_PASSTHROUGH request. */ static int mpi_pre_smp_passthrough(struct mpr_command *cm, struct mpr_usr_command *cmd) { MPI2_SMP_PASSTHROUGH_REQUEST *req = (void *)cm->cm_req; MPI2_SMP_PASSTHROUGH_REPLY *rpl; if (cmd->req_len != sizeof *req) return (EINVAL); if (cmd->rpl_len != sizeof *rpl) return (EINVAL); mpr_init_sge(cm, req, &req->SGL); return (0); } /* * Prepare the mpr_command for a CONFIG request. */ static int mpi_pre_config(struct mpr_command *cm, struct mpr_usr_command *cmd) { MPI2_CONFIG_REQUEST *req = (void *)cm->cm_req; MPI2_CONFIG_REPLY *rpl; if (cmd->req_len != sizeof *req) return (EINVAL); if (cmd->rpl_len != sizeof *rpl) return (EINVAL); mpr_init_sge(cm, req, &req->PageBufferSGE); return (0); } /* * Prepare the mpr_command for a SAS_IO_UNIT_CONTROL request. */ static int mpi_pre_sas_io_unit_control(struct mpr_command *cm, struct mpr_usr_command *cmd) { cm->cm_sge = NULL; cm->cm_sglsize = 0; return (0); } /* * A set of functions to prepare an mpr_command for the various * supported requests. */ struct mpr_user_func { U8 Function; mpr_user_f *f_pre; } mpr_user_func_list[] = { { MPI2_FUNCTION_IOC_FACTS, mpi_pre_ioc_facts }, { MPI2_FUNCTION_PORT_FACTS, mpi_pre_port_facts }, { MPI2_FUNCTION_FW_DOWNLOAD, mpi_pre_fw_download }, { MPI2_FUNCTION_FW_UPLOAD, mpi_pre_fw_upload }, { MPI2_FUNCTION_SATA_PASSTHROUGH, mpi_pre_sata_passthrough }, { MPI2_FUNCTION_SMP_PASSTHROUGH, mpi_pre_smp_passthrough}, { MPI2_FUNCTION_CONFIG, mpi_pre_config}, { MPI2_FUNCTION_SAS_IO_UNIT_CONTROL, mpi_pre_sas_io_unit_control }, { 0xFF, NULL } /* list end */ }; static int mpr_user_setup_request(struct mpr_command *cm, struct mpr_usr_command *cmd) { MPI2_REQUEST_HEADER *hdr = (MPI2_REQUEST_HEADER *)cm->cm_req; struct mpr_user_func *f; for (f = mpr_user_func_list; f->f_pre != NULL; f++) { if (hdr->Function == f->Function) return (f->f_pre(cm, cmd)); } return (EINVAL); } static int mpr_user_command(struct mpr_softc *sc, struct mpr_usr_command *cmd) { MPI2_REQUEST_HEADER *hdr; MPI2_DEFAULT_REPLY *rpl = NULL; void *buf = NULL; struct mpr_command *cm = NULL; int err = 0; int sz; mpr_lock(sc); cm = mpr_alloc_command(sc); if (cm == NULL) { mpr_printf(sc, "%s: no mpr requests\n", __func__); err = ENOMEM; goto RetFree; } mpr_unlock(sc); hdr = (MPI2_REQUEST_HEADER *)cm->cm_req; mpr_dprint(sc, MPR_USER, "%s: req %p %d rpl %p %d\n", __func__, cmd->req, cmd->req_len, cmd->rpl, cmd->rpl_len); if (cmd->req_len > (int)sc->reqframesz) { err = EINVAL; goto RetFreeUnlocked; } err = copyin(cmd->req, hdr, cmd->req_len); if (err != 0) goto RetFreeUnlocked; mpr_dprint(sc, MPR_USER, "%s: Function %02X MsgFlags %02X\n", __func__, hdr->Function, hdr->MsgFlags); if (cmd->len > 0) { buf = malloc(cmd->len, M_MPRUSER, M_WAITOK|M_ZERO); cm->cm_data = buf; cm->cm_length = cmd->len; } else { cm->cm_data = NULL; cm->cm_length = 0; } cm->cm_flags = MPR_CM_FLAGS_SGE_SIMPLE; cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; err = mpr_user_setup_request(cm, cmd); if (err == EINVAL) { mpr_printf(sc, "%s: unsupported parameter or unsupported " "function in request (function = 0x%X)\n", __func__, hdr->Function); } if (err != 0) goto RetFreeUnlocked; mpr_lock(sc); err = mpr_wait_command(sc, &cm, 30, CAN_SLEEP); if (err || (cm == NULL)) { mpr_printf(sc, "%s: invalid request: error %d\n", __func__, err); goto RetFree; } if (cm != NULL) rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply; if (rpl != NULL) sz = rpl->MsgLength * 4; else sz = 0; if (sz > cmd->rpl_len) { mpr_printf(sc, "%s: user reply buffer (%d) smaller than " "returned buffer (%d)\n", __func__, cmd->rpl_len, sz); sz = cmd->rpl_len; } mpr_unlock(sc); copyout(rpl, cmd->rpl, sz); if (buf != NULL) copyout(buf, cmd->buf, cmd->len); mpr_dprint(sc, MPR_USER, "%s: reply size %d\n", __func__, sz); RetFreeUnlocked: mpr_lock(sc); RetFree: if (cm != NULL) mpr_free_command(sc, cm); mpr_unlock(sc); if (buf != NULL) free(buf, M_MPRUSER); return (err); } static int mpr_user_pass_thru(struct mpr_softc *sc, mpr_pass_thru_t *data) { MPI2_REQUEST_HEADER *hdr, tmphdr; MPI2_DEFAULT_REPLY *rpl; Mpi26NVMeEncapsulatedErrorReply_t *nvme_error_reply = NULL; Mpi26NVMeEncapsulatedRequest_t *nvme_encap_request = NULL; struct mpr_command *cm = NULL; int i, err = 0, dir = 0, sz; uint8_t tool, function = 0; u_int sense_len; struct mprsas_target *targ = NULL; /* * Only allow one passthru command at a time. Use the MPR_FLAGS_BUSY * bit to denote that a passthru is being processed. */ mpr_lock(sc); if (sc->mpr_flags & MPR_FLAGS_BUSY) { mpr_dprint(sc, MPR_USER, "%s: Only one passthru command " "allowed at a single time.", __func__); mpr_unlock(sc); return (EBUSY); } sc->mpr_flags |= MPR_FLAGS_BUSY; mpr_unlock(sc); /* * Do some validation on data direction. Valid cases are: * 1) DataSize is 0 and direction is NONE * 2) DataSize is non-zero and one of: * a) direction is READ or * b) direction is WRITE or * c) direction is BOTH and DataOutSize is non-zero * If valid and the direction is BOTH, change the direction to READ. * if valid and the direction is not BOTH, make sure DataOutSize is 0. */ if (((data->DataSize == 0) && (data->DataDirection == MPR_PASS_THRU_DIRECTION_NONE)) || ((data->DataSize != 0) && ((data->DataDirection == MPR_PASS_THRU_DIRECTION_READ) || (data->DataDirection == MPR_PASS_THRU_DIRECTION_WRITE) || ((data->DataDirection == MPR_PASS_THRU_DIRECTION_BOTH) && (data->DataOutSize != 0))))) { if (data->DataDirection == MPR_PASS_THRU_DIRECTION_BOTH) data->DataDirection = MPR_PASS_THRU_DIRECTION_READ; else data->DataOutSize = 0; } else { err = EINVAL; goto RetFreeUnlocked; } mpr_dprint(sc, MPR_USER, "%s: req 0x%jx %d rpl 0x%jx %d " "data in 0x%jx %d data out 0x%jx %d data dir %d\n", __func__, data->PtrRequest, data->RequestSize, data->PtrReply, data->ReplySize, data->PtrData, data->DataSize, data->PtrDataOut, data->DataOutSize, data->DataDirection); /* * copy in the header so we know what we're dealing with before we * commit to allocating a command for it. */ err = copyin(PTRIN(data->PtrRequest), &tmphdr, data->RequestSize); if (err != 0) goto RetFreeUnlocked; if (data->RequestSize > (int)sc->reqframesz) { err = EINVAL; goto RetFreeUnlocked; } function = tmphdr.Function; mpr_dprint(sc, MPR_USER, "%s: Function %02X MsgFlags %02X\n", __func__, function, tmphdr.MsgFlags); /* * Handle a passthru TM request. */ if (function == MPI2_FUNCTION_SCSI_TASK_MGMT) { MPI2_SCSI_TASK_MANAGE_REQUEST *task; mpr_lock(sc); cm = mprsas_alloc_tm(sc); if (cm == NULL) { err = EINVAL; goto Ret; } /* Copy the header in. Only a small fixup is needed. */ task = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req; bcopy(&tmphdr, task, data->RequestSize); task->TaskMID = cm->cm_desc.Default.SMID; cm->cm_data = NULL; cm->cm_complete = NULL; cm->cm_complete_data = NULL; targ = mprsas_find_target_by_handle(sc->sassc, 0, task->DevHandle); if (targ == NULL) { mpr_dprint(sc, MPR_INFO, "%s %d : invalid handle for requested TM 0x%x \n", __func__, __LINE__, task->DevHandle); err = 1; } else { mprsas_prepare_for_tm(sc, cm, targ, CAM_LUN_WILDCARD); err = mpr_wait_command(sc, &cm, 30, CAN_SLEEP); } if (err != 0) { err = EIO; mpr_dprint(sc, MPR_FAULT, "%s: task management failed", __func__); } /* * Copy the reply data and sense data to user space. */ if ((cm != NULL) && (cm->cm_reply != NULL)) { rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply; sz = rpl->MsgLength * 4; if (sz > data->ReplySize) { mpr_printf(sc, "%s: user reply buffer (%d) " "smaller than returned buffer (%d)\n", __func__, data->ReplySize, sz); } mpr_unlock(sc); copyout(cm->cm_reply, PTRIN(data->PtrReply), data->ReplySize); mpr_lock(sc); } mprsas_free_tm(sc, cm); goto Ret; } mpr_lock(sc); cm = mpr_alloc_command(sc); if (cm == NULL) { mpr_printf(sc, "%s: no mpr requests\n", __func__); err = ENOMEM; goto Ret; } mpr_unlock(sc); hdr = (MPI2_REQUEST_HEADER *)cm->cm_req; bcopy(&tmphdr, hdr, data->RequestSize); /* * Do some checking to make sure the IOCTL request contains a valid * request. Then set the SGL info. */ mpr_init_sge(cm, hdr, (void *)((uint8_t *)hdr + data->RequestSize)); /* * Set up for read, write or both. From check above, DataOutSize will * be 0 if direction is READ or WRITE, but it will have some non-zero * value if the direction is BOTH. So, just use the biggest size to get * the cm_data buffer size. If direction is BOTH, 2 SGLs need to be set * up; the first is for the request and the second will contain the * response data. cm_out_len needs to be set here and this will be used * when the SGLs are set up. */ cm->cm_data = NULL; cm->cm_length = MAX(data->DataSize, data->DataOutSize); cm->cm_out_len = data->DataOutSize; cm->cm_flags = 0; if (cm->cm_length != 0) { cm->cm_data = malloc(cm->cm_length, M_MPRUSER, M_WAITOK | M_ZERO); cm->cm_flags = MPR_CM_FLAGS_DATAIN; if (data->DataOutSize) { cm->cm_flags |= MPR_CM_FLAGS_DATAOUT; err = copyin(PTRIN(data->PtrDataOut), cm->cm_data, data->DataOutSize); } else if (data->DataDirection == MPR_PASS_THRU_DIRECTION_WRITE) { cm->cm_flags = MPR_CM_FLAGS_DATAOUT; err = copyin(PTRIN(data->PtrData), cm->cm_data, data->DataSize); } if (err != 0) mpr_dprint(sc, MPR_FAULT, "%s: failed to copy IOCTL " "data from user space\n", __func__); } /* * Set this flag only if processing a command that does not need an * IEEE SGL. The CLI Tool within the Toolbox uses IEEE SGLs, so clear * the flag only for that tool if processing a Toolbox function. */ cm->cm_flags |= MPR_CM_FLAGS_SGE_SIMPLE; for (i = 0; i < sizeof (ieee_sgl_func_list); i++) { if (function == ieee_sgl_func_list[i]) { if (function == MPI2_FUNCTION_TOOLBOX) { tool = (uint8_t)hdr->FunctionDependent1; if (tool != MPI2_TOOLBOX_DIAGNOSTIC_CLI_TOOL) break; } cm->cm_flags &= ~MPR_CM_FLAGS_SGE_SIMPLE; break; } } cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; if (function == MPI2_FUNCTION_NVME_ENCAPSULATED) { nvme_encap_request = (Mpi26NVMeEncapsulatedRequest_t *)cm->cm_req; cm->cm_desc.Default.RequestFlags = MPI26_REQ_DESCRIPT_FLAGS_PCIE_ENCAPSULATED; /* * Get the Physical Address of the sense buffer. * Save the user's Error Response buffer address and use that * field to hold the sense buffer address. * Clear the internal sense buffer, which will potentially hold * the Completion Queue Entry on return, or 0 if no Entry. * Build the PRPs and set direction bits. * Send the request. */ cm->nvme_error_response = (uint64_t *)(uintptr_t)(((uint64_t)nvme_encap_request-> ErrorResponseBaseAddress.High << 32) | (uint64_t)nvme_encap_request-> ErrorResponseBaseAddress.Low); nvme_encap_request->ErrorResponseBaseAddress.High = htole32((uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32)); nvme_encap_request->ErrorResponseBaseAddress.Low = htole32(cm->cm_sense_busaddr); memset(cm->cm_sense, 0, NVME_ERROR_RESPONSE_SIZE); mpr_build_nvme_prp(sc, cm, nvme_encap_request, cm->cm_data, data->DataSize, data->DataOutSize); } /* * Set up Sense buffer and SGL offset for IO passthru. SCSI IO request * uses SCSI IO or Fast Path SCSI IO descriptor. */ if ((function == MPI2_FUNCTION_SCSI_IO_REQUEST) || (function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { MPI2_SCSI_IO_REQUEST *scsi_io_req; scsi_io_req = (MPI2_SCSI_IO_REQUEST *)hdr; /* * Put SGE for data and data_out buffer at the end of * scsi_io_request message header (64 bytes in total). * Following above SGEs, the residual space will be used by * sense data. */ scsi_io_req->SenseBufferLength = (uint8_t)(data->RequestSize - 64); scsi_io_req->SenseBufferLowAddress = htole32(cm->cm_sense_busaddr); /* * Set SGLOffset0 value. This is the number of dwords that SGL * is offset from the beginning of MPI2_SCSI_IO_REQUEST struct. */ scsi_io_req->SGLOffset0 = 24; /* * Setup descriptor info. RAID passthrough must use the * default request descriptor which is already set, so if this * is a SCSI IO request, change the descriptor to SCSI IO or * Fast Path SCSI IO. Also, if this is a SCSI IO request, * handle the reply in the mprsas_scsio_complete function. */ if (function == MPI2_FUNCTION_SCSI_IO_REQUEST) { targ = mprsas_find_target_by_handle(sc->sassc, 0, scsi_io_req->DevHandle); if (!targ) { printf("No Target found for handle %d\n", scsi_io_req->DevHandle); err = EINVAL; goto RetFreeUnlocked; } if (targ->scsi_req_desc_type == MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO) { cm->cm_desc.FastPathSCSIIO.RequestFlags = MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO; if (!sc->atomic_desc_capable) { cm->cm_desc.FastPathSCSIIO.DevHandle = scsi_io_req->DevHandle; } scsi_io_req->IoFlags |= MPI25_SCSIIO_IOFLAGS_FAST_PATH; } else { cm->cm_desc.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; if (!sc->atomic_desc_capable) { cm->cm_desc.SCSIIO.DevHandle = scsi_io_req->DevHandle; } } /* * Make sure the DevHandle is not 0 because this is a * likely error. */ if (scsi_io_req->DevHandle == 0) { err = EINVAL; goto RetFreeUnlocked; } } } mpr_lock(sc); err = mpr_wait_command(sc, &cm, 30, CAN_SLEEP); if (err || (cm == NULL)) { mpr_printf(sc, "%s: invalid request: error %d\n", __func__, err); goto RetFree; } /* * Sync the DMA data, if any. Then copy the data to user space. */ if (cm->cm_data != NULL) { if (cm->cm_flags & MPR_CM_FLAGS_DATAIN) dir = BUS_DMASYNC_POSTREAD; else if (cm->cm_flags & MPR_CM_FLAGS_DATAOUT) dir = BUS_DMASYNC_POSTWRITE; bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, dir); bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap); if (cm->cm_flags & MPR_CM_FLAGS_DATAIN) { mpr_unlock(sc); err = copyout(cm->cm_data, PTRIN(data->PtrData), data->DataSize); mpr_lock(sc); if (err != 0) mpr_dprint(sc, MPR_FAULT, "%s: failed to copy " "IOCTL data to user space\n", __func__); } } /* * Copy the reply data and sense data to user space. */ if (cm->cm_reply != NULL) { rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply; sz = rpl->MsgLength * 4; if (sz > data->ReplySize) { mpr_printf(sc, "%s: user reply buffer (%d) smaller " "than returned buffer (%d)\n", __func__, data->ReplySize, sz); } mpr_unlock(sc); copyout(cm->cm_reply, PTRIN(data->PtrReply), data->ReplySize); mpr_lock(sc); if ((function == MPI2_FUNCTION_SCSI_IO_REQUEST) || (function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { if (((MPI2_SCSI_IO_REPLY *)rpl)->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_VALID) { sense_len = MIN((le32toh(((MPI2_SCSI_IO_REPLY *)rpl)-> SenseCount)), sizeof(struct scsi_sense_data)); mpr_unlock(sc); copyout(cm->cm_sense, (PTRIN(data->PtrReply + sizeof(MPI2_SCSI_IO_REPLY))), sense_len); mpr_lock(sc); } } /* * Copy out the NVMe Error Reponse to user. The Error Response * buffer is given by the user, but a sense buffer is used to * get that data from the IOC. The user's * ErrorResponseBaseAddress is saved in the * 'nvme_error_response' field before the command because that * field is set to a sense buffer. When the command is * complete, the Error Response data from the IOC is copied to * that user address after it is checked for validity. * Also note that 'sense' buffers are not defined for * NVMe commands. Sense terminalogy is only used here so that * the same IOCTL structure and sense buffers can be used for * NVMe. */ if (function == MPI2_FUNCTION_NVME_ENCAPSULATED) { if (cm->nvme_error_response == NULL) { mpr_dprint(sc, MPR_INFO, "NVMe Error Response " "buffer is NULL. Response data will not be " "returned.\n"); mpr_unlock(sc); goto RetFreeUnlocked; } nvme_error_reply = (Mpi26NVMeEncapsulatedErrorReply_t *)cm->cm_reply; sz = MIN(le32toh(nvme_error_reply->ErrorResponseCount), NVME_ERROR_RESPONSE_SIZE); mpr_unlock(sc); copyout(cm->cm_sense, (PTRIN(data->PtrReply + sizeof(MPI2_SCSI_IO_REPLY))), sz); mpr_lock(sc); } } mpr_unlock(sc); RetFreeUnlocked: mpr_lock(sc); RetFree: if (cm != NULL) { if (cm->cm_data) free(cm->cm_data, M_MPRUSER); mpr_free_command(sc, cm); } Ret: sc->mpr_flags &= ~MPR_FLAGS_BUSY; mpr_unlock(sc); return (err); } static void mpr_user_get_adapter_data(struct mpr_softc *sc, mpr_adapter_data_t *data) { Mpi2ConfigReply_t mpi_reply; Mpi2BiosPage3_t config_page; /* * Use the PCI interface functions to get the Bus, Device, and Function * information. */ data->PciInformation.u.bits.BusNumber = pci_get_bus(sc->mpr_dev); data->PciInformation.u.bits.DeviceNumber = pci_get_slot(sc->mpr_dev); data->PciInformation.u.bits.FunctionNumber = pci_get_function(sc->mpr_dev); /* * Get the FW version that should already be saved in IOC Facts. */ data->MpiFirmwareVersion = sc->facts->FWVersion.Word; /* * General device info. */ if (sc->mpr_flags & MPR_FLAGS_GEN35_IOC) data->AdapterType = MPRIOCTL_ADAPTER_TYPE_SAS35; else data->AdapterType = MPRIOCTL_ADAPTER_TYPE_SAS3; data->PCIDeviceHwId = pci_get_device(sc->mpr_dev); data->PCIDeviceHwRev = pci_read_config(sc->mpr_dev, PCIR_REVID, 1); data->SubSystemId = pci_get_subdevice(sc->mpr_dev); data->SubsystemVendorId = pci_get_subvendor(sc->mpr_dev); /* * Get the driver version. */ strcpy((char *)&data->DriverVersion[0], MPR_DRIVER_VERSION); /* * Need to get BIOS Config Page 3 for the BIOS Version. */ data->BiosVersion = 0; mpr_lock(sc); if (mpr_config_get_bios_pg3(sc, &mpi_reply, &config_page)) printf("%s: Error while retrieving BIOS Version\n", __func__); else data->BiosVersion = config_page.BiosVersion; mpr_unlock(sc); } static void mpr_user_read_pci_info(struct mpr_softc *sc, mpr_pci_info_t *data) { int i; /* * Use the PCI interface functions to get the Bus, Device, and Function * information. */ data->BusNumber = pci_get_bus(sc->mpr_dev); data->DeviceNumber = pci_get_slot(sc->mpr_dev); data->FunctionNumber = pci_get_function(sc->mpr_dev); /* * Now get the interrupt vector and the pci header. The vector can * only be 0 right now. The header is the first 256 bytes of config * space. */ data->InterruptVector = 0; for (i = 0; i < sizeof (data->PciHeader); i++) { data->PciHeader[i] = pci_read_config(sc->mpr_dev, i, 1); } } static uint8_t mpr_get_fw_diag_buffer_number(struct mpr_softc *sc, uint32_t unique_id) { uint8_t index; for (index = 0; index < MPI2_DIAG_BUF_TYPE_COUNT; index++) { if (sc->fw_diag_buffer_list[index].unique_id == unique_id) { return (index); } } return (MPR_FW_DIAGNOSTIC_UID_NOT_FOUND); } static int mpr_post_fw_diag_buffer(struct mpr_softc *sc, mpr_fw_diagnostic_buffer_t *pBuffer, uint32_t *return_code) { MPI2_DIAG_BUFFER_POST_REQUEST *req; MPI2_DIAG_BUFFER_POST_REPLY *reply; struct mpr_command *cm = NULL; int i, status; /* * If buffer is not enabled, just leave. */ *return_code = MPR_FW_DIAG_ERROR_POST_FAILED; if (!pBuffer->enabled) { return (MPR_DIAG_FAILURE); } /* * Clear some flags initially. */ pBuffer->force_release = FALSE; pBuffer->valid_data = FALSE; pBuffer->owned_by_firmware = FALSE; /* * Get a command. */ cm = mpr_alloc_command(sc); if (cm == NULL) { mpr_printf(sc, "%s: no mpr requests\n", __func__); return (MPR_DIAG_FAILURE); } /* * Build the request for releasing the FW Diag Buffer and send it. */ req = (MPI2_DIAG_BUFFER_POST_REQUEST *)cm->cm_req; req->Function = MPI2_FUNCTION_DIAG_BUFFER_POST; req->BufferType = pBuffer->buffer_type; req->ExtendedType = pBuffer->extended_type; req->BufferLength = pBuffer->size; for (i = 0; i < (sizeof(req->ProductSpecific) / 4); i++) req->ProductSpecific[i] = pBuffer->product_specific[i]; mpr_from_u64(sc->fw_diag_busaddr, &req->BufferAddress); cm->cm_data = NULL; cm->cm_length = 0; cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; cm->cm_complete_data = NULL; /* * Send command synchronously. */ status = mpr_wait_command(sc, &cm, 30, CAN_SLEEP); if (status || (cm == NULL)) { mpr_printf(sc, "%s: invalid request: error %d\n", __func__, status); status = MPR_DIAG_FAILURE; goto done; } /* * Process POST reply. */ reply = (MPI2_DIAG_BUFFER_POST_REPLY *)cm->cm_reply; if (reply == NULL) { mpr_printf(sc, "%s: reply is NULL, probably due to " "reinitialization", __func__); status = MPR_DIAG_FAILURE; goto done; } if ((le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS) { status = MPR_DIAG_FAILURE; mpr_dprint(sc, MPR_FAULT, "%s: post of FW Diag Buffer failed " "with IOCStatus = 0x%x, IOCLogInfo = 0x%x and " "TransferLength = 0x%x\n", __func__, le16toh(reply->IOCStatus), le32toh(reply->IOCLogInfo), le32toh(reply->TransferLength)); goto done; } /* * Post was successful. */ pBuffer->valid_data = TRUE; pBuffer->owned_by_firmware = TRUE; *return_code = MPR_FW_DIAG_ERROR_SUCCESS; status = MPR_DIAG_SUCCESS; done: if (cm != NULL) mpr_free_command(sc, cm); return (status); } static int mpr_release_fw_diag_buffer(struct mpr_softc *sc, mpr_fw_diagnostic_buffer_t *pBuffer, uint32_t *return_code, uint32_t diag_type) { MPI2_DIAG_RELEASE_REQUEST *req; MPI2_DIAG_RELEASE_REPLY *reply; struct mpr_command *cm = NULL; int status; /* * If buffer is not enabled, just leave. */ *return_code = MPR_FW_DIAG_ERROR_RELEASE_FAILED; if (!pBuffer->enabled) { mpr_dprint(sc, MPR_USER, "%s: This buffer type is not " "supported by the IOC", __func__); return (MPR_DIAG_FAILURE); } /* * Clear some flags initially. */ pBuffer->force_release = FALSE; pBuffer->valid_data = FALSE; pBuffer->owned_by_firmware = FALSE; /* * Get a command. */ cm = mpr_alloc_command(sc); if (cm == NULL) { mpr_printf(sc, "%s: no mpr requests\n", __func__); return (MPR_DIAG_FAILURE); } /* * Build the request for releasing the FW Diag Buffer and send it. */ req = (MPI2_DIAG_RELEASE_REQUEST *)cm->cm_req; req->Function = MPI2_FUNCTION_DIAG_RELEASE; req->BufferType = pBuffer->buffer_type; cm->cm_data = NULL; cm->cm_length = 0; cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; cm->cm_complete_data = NULL; /* * Send command synchronously. */ status = mpr_wait_command(sc, &cm, 30, CAN_SLEEP); if (status || (cm == NULL)) { mpr_printf(sc, "%s: invalid request: error %d\n", __func__, status); status = MPR_DIAG_FAILURE; goto done; } /* * Process RELEASE reply. */ reply = (MPI2_DIAG_RELEASE_REPLY *)cm->cm_reply; if (reply == NULL) { mpr_printf(sc, "%s: reply is NULL, probably due to " "reinitialization", __func__); status = MPR_DIAG_FAILURE; goto done; } if (((le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS) || pBuffer->owned_by_firmware) { status = MPR_DIAG_FAILURE; mpr_dprint(sc, MPR_FAULT, "%s: release of FW Diag Buffer " "failed with IOCStatus = 0x%x and IOCLogInfo = 0x%x\n", __func__, le16toh(reply->IOCStatus), le32toh(reply->IOCLogInfo)); goto done; } /* * Release was successful. */ *return_code = MPR_FW_DIAG_ERROR_SUCCESS; status = MPR_DIAG_SUCCESS; /* * If this was for an UNREGISTER diag type command, clear the unique ID. */ if (diag_type == MPR_FW_DIAG_TYPE_UNREGISTER) { pBuffer->unique_id = MPR_FW_DIAG_INVALID_UID; } done: if (cm != NULL) mpr_free_command(sc, cm); return (status); } static int mpr_diag_register(struct mpr_softc *sc, mpr_fw_diag_register_t *diag_register, uint32_t *return_code) { mpr_fw_diagnostic_buffer_t *pBuffer; struct mpr_busdma_context *ctx; uint8_t extended_type, buffer_type, i; uint32_t buffer_size; uint32_t unique_id; int status; int error; extended_type = diag_register->ExtendedType; buffer_type = diag_register->BufferType; buffer_size = diag_register->RequestedBufferSize; unique_id = diag_register->UniqueId; ctx = NULL; error = 0; /* * Check for valid buffer type */ if (buffer_type >= MPI2_DIAG_BUF_TYPE_COUNT) { *return_code = MPR_FW_DIAG_ERROR_INVALID_PARAMETER; return (MPR_DIAG_FAILURE); } /* * Get the current buffer and look up the unique ID. The unique ID * should not be found. If it is, the ID is already in use. */ i = mpr_get_fw_diag_buffer_number(sc, unique_id); pBuffer = &sc->fw_diag_buffer_list[buffer_type]; if (i != MPR_FW_DIAGNOSTIC_UID_NOT_FOUND) { *return_code = MPR_FW_DIAG_ERROR_INVALID_UID; return (MPR_DIAG_FAILURE); } /* * The buffer's unique ID should not be registered yet, and the given * unique ID cannot be 0. */ if ((pBuffer->unique_id != MPR_FW_DIAG_INVALID_UID) || (unique_id == MPR_FW_DIAG_INVALID_UID)) { *return_code = MPR_FW_DIAG_ERROR_INVALID_UID; return (MPR_DIAG_FAILURE); } /* * If this buffer is already posted as immediate, just change owner. */ if (pBuffer->immediate && pBuffer->owned_by_firmware && (pBuffer->unique_id == MPR_FW_DIAG_INVALID_UID)) { pBuffer->immediate = FALSE; pBuffer->unique_id = unique_id; return (MPR_DIAG_SUCCESS); } /* * Post a new buffer after checking if it's enabled. The DMA buffer * that is allocated will be contiguous (nsegments = 1). */ if (!pBuffer->enabled) { *return_code = MPR_FW_DIAG_ERROR_NO_BUFFER; return (MPR_DIAG_FAILURE); } if (bus_dma_tag_create( sc->mpr_parent_dmat, /* parent */ 1, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ buffer_size, /* maxsize */ 1, /* nsegments */ buffer_size, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->fw_diag_dmat)) { mpr_dprint(sc, MPR_ERROR, "Cannot allocate FW diag buffer DMA tag\n"); *return_code = MPR_FW_DIAG_ERROR_NO_BUFFER; status = MPR_DIAG_FAILURE; goto bailout; } if (bus_dmamem_alloc(sc->fw_diag_dmat, (void **)&sc->fw_diag_buffer, BUS_DMA_NOWAIT, &sc->fw_diag_map)) { mpr_dprint(sc, MPR_ERROR, "Cannot allocate FW diag buffer memory\n"); *return_code = MPR_FW_DIAG_ERROR_NO_BUFFER; status = MPR_DIAG_FAILURE; goto bailout; } bzero(sc->fw_diag_buffer, buffer_size); ctx = malloc(sizeof(*ctx), M_MPR, M_WAITOK | M_ZERO); if (ctx == NULL) { device_printf(sc->mpr_dev, "%s: context malloc failed\n", __func__); *return_code = MPR_FW_DIAG_ERROR_NO_BUFFER; status = MPR_DIAG_FAILURE; goto bailout; } ctx->addr = &sc->fw_diag_busaddr; ctx->buffer_dmat = sc->fw_diag_dmat; ctx->buffer_dmamap = sc->fw_diag_map; ctx->softc = sc; error = bus_dmamap_load(sc->fw_diag_dmat, sc->fw_diag_map, sc->fw_diag_buffer, buffer_size, mpr_memaddr_wait_cb, ctx, 0); if (error == EINPROGRESS) { /* XXX KDM */ device_printf(sc->mpr_dev, "%s: Deferred bus_dmamap_load\n", __func__); /* * Wait for the load to complete. If we're interrupted, * bail out. */ mpr_lock(sc); if (ctx->completed == 0) { error = msleep(ctx, &sc->mpr_mtx, PCATCH, "mprwait", 0); if (error != 0) { /* * We got an error from msleep(9). This is * most likely due to a signal. Tell * mpr_memaddr_wait_cb() that we've abandoned * the context, so it needs to clean up when * it is called. */ ctx->abandoned = 1; /* The callback will free this memory */ ctx = NULL; mpr_unlock(sc); device_printf(sc->mpr_dev, "Cannot " "bus_dmamap_load FW diag buffer, error = " "%d returned from msleep\n", error); *return_code = MPR_FW_DIAG_ERROR_NO_BUFFER; status = MPR_DIAG_FAILURE; goto bailout; } } mpr_unlock(sc); } if ((error != 0) || (ctx->error != 0)) { device_printf(sc->mpr_dev, "Cannot bus_dmamap_load FW diag " "buffer, %serror = %d\n", error ? "" : "callback ", error ? error : ctx->error); *return_code = MPR_FW_DIAG_ERROR_NO_BUFFER; status = MPR_DIAG_FAILURE; goto bailout; } bus_dmamap_sync(sc->fw_diag_dmat, sc->fw_diag_map, BUS_DMASYNC_PREREAD); pBuffer->size = buffer_size; /* * Copy the given info to the diag buffer and post the buffer. */ pBuffer->buffer_type = buffer_type; pBuffer->immediate = FALSE; if (buffer_type == MPI2_DIAG_BUF_TYPE_TRACE) { for (i = 0; i < (sizeof (pBuffer->product_specific) / 4); i++) { pBuffer->product_specific[i] = diag_register->ProductSpecific[i]; } } pBuffer->extended_type = extended_type; pBuffer->unique_id = unique_id; status = mpr_post_fw_diag_buffer(sc, pBuffer, return_code); bailout: /* * In case there was a failure, free the DMA buffer. */ if (status == MPR_DIAG_FAILURE) { if (sc->fw_diag_busaddr != 0) { bus_dmamap_unload(sc->fw_diag_dmat, sc->fw_diag_map); sc->fw_diag_busaddr = 0; } if (sc->fw_diag_buffer != NULL) { bus_dmamem_free(sc->fw_diag_dmat, sc->fw_diag_buffer, sc->fw_diag_map); sc->fw_diag_buffer = NULL; } if (sc->fw_diag_dmat != NULL) { bus_dma_tag_destroy(sc->fw_diag_dmat); sc->fw_diag_dmat = NULL; } } if (ctx != NULL) free(ctx, M_MPR); return (status); } static int mpr_diag_unregister(struct mpr_softc *sc, mpr_fw_diag_unregister_t *diag_unregister, uint32_t *return_code) { mpr_fw_diagnostic_buffer_t *pBuffer; uint8_t i; uint32_t unique_id; int status; unique_id = diag_unregister->UniqueId; /* * Get the current buffer and look up the unique ID. The unique ID * should be there. */ i = mpr_get_fw_diag_buffer_number(sc, unique_id); if (i == MPR_FW_DIAGNOSTIC_UID_NOT_FOUND) { *return_code = MPR_FW_DIAG_ERROR_INVALID_UID; return (MPR_DIAG_FAILURE); } pBuffer = &sc->fw_diag_buffer_list[i]; /* * Try to release the buffer from FW before freeing it. If release * fails, don't free the DMA buffer in case FW tries to access it * later. If buffer is not owned by firmware, can't release it. */ if (!pBuffer->owned_by_firmware) { status = MPR_DIAG_SUCCESS; } else { status = mpr_release_fw_diag_buffer(sc, pBuffer, return_code, MPR_FW_DIAG_TYPE_UNREGISTER); } /* * At this point, return the current status no matter what happens with * the DMA buffer. */ pBuffer->unique_id = MPR_FW_DIAG_INVALID_UID; if (status == MPR_DIAG_SUCCESS) { if (sc->fw_diag_busaddr != 0) { bus_dmamap_unload(sc->fw_diag_dmat, sc->fw_diag_map); sc->fw_diag_busaddr = 0; } if (sc->fw_diag_buffer != NULL) { bus_dmamem_free(sc->fw_diag_dmat, sc->fw_diag_buffer, sc->fw_diag_map); sc->fw_diag_buffer = NULL; } if (sc->fw_diag_dmat != NULL) { bus_dma_tag_destroy(sc->fw_diag_dmat); sc->fw_diag_dmat = NULL; } } return (status); } static int mpr_diag_query(struct mpr_softc *sc, mpr_fw_diag_query_t *diag_query, uint32_t *return_code) { mpr_fw_diagnostic_buffer_t *pBuffer; uint8_t i; uint32_t unique_id; unique_id = diag_query->UniqueId; /* * If ID is valid, query on ID. * If ID is invalid, query on buffer type. */ if (unique_id == MPR_FW_DIAG_INVALID_UID) { i = diag_query->BufferType; if (i >= MPI2_DIAG_BUF_TYPE_COUNT) { *return_code = MPR_FW_DIAG_ERROR_INVALID_UID; return (MPR_DIAG_FAILURE); } } else { i = mpr_get_fw_diag_buffer_number(sc, unique_id); if (i == MPR_FW_DIAGNOSTIC_UID_NOT_FOUND) { *return_code = MPR_FW_DIAG_ERROR_INVALID_UID; return (MPR_DIAG_FAILURE); } } /* * Fill query structure with the diag buffer info. */ pBuffer = &sc->fw_diag_buffer_list[i]; diag_query->BufferType = pBuffer->buffer_type; diag_query->ExtendedType = pBuffer->extended_type; if (diag_query->BufferType == MPI2_DIAG_BUF_TYPE_TRACE) { for (i = 0; i < (sizeof(diag_query->ProductSpecific) / 4); i++) { diag_query->ProductSpecific[i] = pBuffer->product_specific[i]; } } diag_query->TotalBufferSize = pBuffer->size; diag_query->DriverAddedBufferSize = 0; diag_query->UniqueId = pBuffer->unique_id; diag_query->ApplicationFlags = 0; diag_query->DiagnosticFlags = 0; /* * Set/Clear application flags */ if (pBuffer->immediate) { diag_query->ApplicationFlags &= ~MPR_FW_DIAG_FLAG_APP_OWNED; } else { diag_query->ApplicationFlags |= MPR_FW_DIAG_FLAG_APP_OWNED; } if (pBuffer->valid_data || pBuffer->owned_by_firmware) { diag_query->ApplicationFlags |= MPR_FW_DIAG_FLAG_BUFFER_VALID; } else { diag_query->ApplicationFlags &= ~MPR_FW_DIAG_FLAG_BUFFER_VALID; } if (pBuffer->owned_by_firmware) { diag_query->ApplicationFlags |= MPR_FW_DIAG_FLAG_FW_BUFFER_ACCESS; } else { diag_query->ApplicationFlags &= ~MPR_FW_DIAG_FLAG_FW_BUFFER_ACCESS; } return (MPR_DIAG_SUCCESS); } static int mpr_diag_read_buffer(struct mpr_softc *sc, mpr_diag_read_buffer_t *diag_read_buffer, uint8_t *ioctl_buf, uint32_t *return_code) { mpr_fw_diagnostic_buffer_t *pBuffer; uint8_t i, *pData; uint32_t unique_id; int status; unique_id = diag_read_buffer->UniqueId; /* * Get the current buffer and look up the unique ID. The unique ID * should be there. */ i = mpr_get_fw_diag_buffer_number(sc, unique_id); if (i == MPR_FW_DIAGNOSTIC_UID_NOT_FOUND) { *return_code = MPR_FW_DIAG_ERROR_INVALID_UID; return (MPR_DIAG_FAILURE); } pBuffer = &sc->fw_diag_buffer_list[i]; /* * Make sure requested read is within limits */ if (diag_read_buffer->StartingOffset + diag_read_buffer->BytesToRead > pBuffer->size) { *return_code = MPR_FW_DIAG_ERROR_INVALID_PARAMETER; return (MPR_DIAG_FAILURE); } /* Sync the DMA map before we copy to userland. */ bus_dmamap_sync(sc->fw_diag_dmat, sc->fw_diag_map, BUS_DMASYNC_POSTREAD); /* * Copy the requested data from DMA to the diag_read_buffer. The DMA * buffer that was allocated is one contiguous buffer. */ pData = (uint8_t *)(sc->fw_diag_buffer + diag_read_buffer->StartingOffset); if (copyout(pData, ioctl_buf, diag_read_buffer->BytesToRead) != 0) return (MPR_DIAG_FAILURE); diag_read_buffer->Status = 0; /* * Set or clear the Force Release flag. */ if (pBuffer->force_release) { diag_read_buffer->Flags |= MPR_FW_DIAG_FLAG_FORCE_RELEASE; } else { diag_read_buffer->Flags &= ~MPR_FW_DIAG_FLAG_FORCE_RELEASE; } /* * If buffer is to be reregistered, make sure it's not already owned by * firmware first. */ status = MPR_DIAG_SUCCESS; if (!pBuffer->owned_by_firmware) { if (diag_read_buffer->Flags & MPR_FW_DIAG_FLAG_REREGISTER) { status = mpr_post_fw_diag_buffer(sc, pBuffer, return_code); } } return (status); } static int mpr_diag_release(struct mpr_softc *sc, mpr_fw_diag_release_t *diag_release, uint32_t *return_code) { mpr_fw_diagnostic_buffer_t *pBuffer; uint8_t i; uint32_t unique_id; int status; unique_id = diag_release->UniqueId; /* * Get the current buffer and look up the unique ID. The unique ID * should be there. */ i = mpr_get_fw_diag_buffer_number(sc, unique_id); if (i == MPR_FW_DIAGNOSTIC_UID_NOT_FOUND) { *return_code = MPR_FW_DIAG_ERROR_INVALID_UID; return (MPR_DIAG_FAILURE); } pBuffer = &sc->fw_diag_buffer_list[i]; /* * If buffer is not owned by firmware, it's already been released. */ if (!pBuffer->owned_by_firmware) { *return_code = MPR_FW_DIAG_ERROR_ALREADY_RELEASED; return (MPR_DIAG_FAILURE); } /* * Release the buffer. */ status = mpr_release_fw_diag_buffer(sc, pBuffer, return_code, MPR_FW_DIAG_TYPE_RELEASE); return (status); } static int mpr_do_diag_action(struct mpr_softc *sc, uint32_t action, uint8_t *diag_action, uint32_t length, uint32_t *return_code) { mpr_fw_diag_register_t diag_register; mpr_fw_diag_unregister_t diag_unregister; mpr_fw_diag_query_t diag_query; mpr_diag_read_buffer_t diag_read_buffer; mpr_fw_diag_release_t diag_release; int status = MPR_DIAG_SUCCESS; uint32_t original_return_code; original_return_code = *return_code; *return_code = MPR_FW_DIAG_ERROR_SUCCESS; switch (action) { case MPR_FW_DIAG_TYPE_REGISTER: if (!length) { *return_code = MPR_FW_DIAG_ERROR_INVALID_PARAMETER; status = MPR_DIAG_FAILURE; break; } if (copyin(diag_action, &diag_register, sizeof(diag_register)) != 0) return (MPR_DIAG_FAILURE); status = mpr_diag_register(sc, &diag_register, return_code); break; case MPR_FW_DIAG_TYPE_UNREGISTER: if (length < sizeof(diag_unregister)) { *return_code = MPR_FW_DIAG_ERROR_INVALID_PARAMETER; status = MPR_DIAG_FAILURE; break; } if (copyin(diag_action, &diag_unregister, sizeof(diag_unregister)) != 0) return (MPR_DIAG_FAILURE); status = mpr_diag_unregister(sc, &diag_unregister, return_code); break; case MPR_FW_DIAG_TYPE_QUERY: if (length < sizeof (diag_query)) { *return_code = MPR_FW_DIAG_ERROR_INVALID_PARAMETER; status = MPR_DIAG_FAILURE; break; } if (copyin(diag_action, &diag_query, sizeof(diag_query)) != 0) return (MPR_DIAG_FAILURE); status = mpr_diag_query(sc, &diag_query, return_code); if (status == MPR_DIAG_SUCCESS) if (copyout(&diag_query, diag_action, sizeof (diag_query)) != 0) return (MPR_DIAG_FAILURE); break; case MPR_FW_DIAG_TYPE_READ_BUFFER: if (copyin(diag_action, &diag_read_buffer, sizeof(diag_read_buffer)) != 0) return (MPR_DIAG_FAILURE); if (length < diag_read_buffer.BytesToRead) { *return_code = MPR_FW_DIAG_ERROR_INVALID_PARAMETER; status = MPR_DIAG_FAILURE; break; } status = mpr_diag_read_buffer(sc, &diag_read_buffer, PTRIN(diag_read_buffer.PtrDataBuffer), return_code); if (status == MPR_DIAG_SUCCESS) { if (copyout(&diag_read_buffer, diag_action, sizeof(diag_read_buffer) - sizeof(diag_read_buffer.PtrDataBuffer)) != 0) return (MPR_DIAG_FAILURE); } break; case MPR_FW_DIAG_TYPE_RELEASE: if (length < sizeof(diag_release)) { *return_code = MPR_FW_DIAG_ERROR_INVALID_PARAMETER; status = MPR_DIAG_FAILURE; break; } if (copyin(diag_action, &diag_release, sizeof(diag_release)) != 0) return (MPR_DIAG_FAILURE); status = mpr_diag_release(sc, &diag_release, return_code); break; default: *return_code = MPR_FW_DIAG_ERROR_INVALID_PARAMETER; status = MPR_DIAG_FAILURE; break; } if ((status == MPR_DIAG_FAILURE) && (original_return_code == MPR_FW_DIAG_NEW) && (*return_code != MPR_FW_DIAG_ERROR_SUCCESS)) status = MPR_DIAG_SUCCESS; return (status); } static int mpr_user_diag_action(struct mpr_softc *sc, mpr_diag_action_t *data) { int status; /* * Only allow one diag action at one time. */ if (sc->mpr_flags & MPR_FLAGS_BUSY) { mpr_dprint(sc, MPR_USER, "%s: Only one FW diag command " "allowed at a single time.", __func__); return (EBUSY); } sc->mpr_flags |= MPR_FLAGS_BUSY; /* * Send diag action request */ if (data->Action == MPR_FW_DIAG_TYPE_REGISTER || data->Action == MPR_FW_DIAG_TYPE_UNREGISTER || data->Action == MPR_FW_DIAG_TYPE_QUERY || data->Action == MPR_FW_DIAG_TYPE_READ_BUFFER || data->Action == MPR_FW_DIAG_TYPE_RELEASE) { status = mpr_do_diag_action(sc, data->Action, PTRIN(data->PtrDiagAction), data->Length, &data->ReturnCode); } else status = EINVAL; sc->mpr_flags &= ~MPR_FLAGS_BUSY; return (status); } /* * Copy the event recording mask and the event queue size out. For * clarification, the event recording mask (events_to_record) is not the same * thing as the event mask (event_mask). events_to_record has a bit set for * every event type that is to be recorded by the driver, and event_mask has a * bit cleared for every event that is allowed into the driver from the IOC. * They really have nothing to do with each other. */ static void mpr_user_event_query(struct mpr_softc *sc, mpr_event_query_t *data) { uint8_t i; mpr_lock(sc); data->Entries = MPR_EVENT_QUEUE_SIZE; for (i = 0; i < 4; i++) { data->Types[i] = sc->events_to_record[i]; } mpr_unlock(sc); } /* * Set the driver's event mask according to what's been given. See * mpr_user_event_query for explanation of the event recording mask and the IOC * event mask. It's the app's responsibility to enable event logging by setting * the bits in events_to_record. Initially, no events will be logged. */ static void mpr_user_event_enable(struct mpr_softc *sc, mpr_event_enable_t *data) { uint8_t i; mpr_lock(sc); for (i = 0; i < 4; i++) { sc->events_to_record[i] = data->Types[i]; } mpr_unlock(sc); } /* * Copy out the events that have been recorded, up to the max events allowed. */ static int mpr_user_event_report(struct mpr_softc *sc, mpr_event_report_t *data) { int status = 0; uint32_t size; mpr_lock(sc); size = data->Size; if ((size >= sizeof(sc->recorded_events)) && (status == 0)) { mpr_unlock(sc); if (copyout((void *)sc->recorded_events, PTRIN(data->PtrEvents), size) != 0) status = EFAULT; mpr_lock(sc); } else { /* * data->Size value is not large enough to copy event data. */ status = EFAULT; } /* * Change size value to match the number of bytes that were copied. */ if (status == 0) data->Size = sizeof(sc->recorded_events); mpr_unlock(sc); return (status); } /* * Record events into the driver from the IOC if they are not masked. */ void mprsas_record_event(struct mpr_softc *sc, MPI2_EVENT_NOTIFICATION_REPLY *event_reply) { uint32_t event; int i, j; uint16_t event_data_len; boolean_t sendAEN = FALSE; event = event_reply->Event; /* * Generate a system event to let anyone who cares know that a * LOG_ENTRY_ADDED event has occurred. This is sent no matter what the * event mask is set to. */ if (event == MPI2_EVENT_LOG_ENTRY_ADDED) { sendAEN = TRUE; } /* * Record the event only if its corresponding bit is set in * events_to_record. event_index is the index into recorded_events and * event_number is the overall number of an event being recorded since * start-of-day. event_index will roll over; event_number will never * roll over. */ i = (uint8_t)(event / 32); j = (uint8_t)(event % 32); if ((i < 4) && ((1 << j) & sc->events_to_record[i])) { i = sc->event_index; sc->recorded_events[i].Type = event; sc->recorded_events[i].Number = ++sc->event_number; bzero(sc->recorded_events[i].Data, MPR_MAX_EVENT_DATA_LENGTH * 4); event_data_len = event_reply->EventDataLength; if (event_data_len > 0) { /* * Limit data to size in m_event entry */ if (event_data_len > MPR_MAX_EVENT_DATA_LENGTH) { event_data_len = MPR_MAX_EVENT_DATA_LENGTH; } for (j = 0; j < event_data_len; j++) { sc->recorded_events[i].Data[j] = event_reply->EventData[j]; } /* * check for index wrap-around */ if (++i == MPR_EVENT_QUEUE_SIZE) { i = 0; } sc->event_index = (uint8_t)i; /* * Set flag to send the event. */ sendAEN = TRUE; } } /* * Generate a system event if flag is set to let anyone who cares know * that an event has occurred. */ if (sendAEN) { //SLM-how to send a system event (see kqueue, kevent) // (void) ddi_log_sysevent(mpt->m_dip, DDI_VENDOR_LSI, "MPT_SAS", // "SAS", NULL, NULL, DDI_NOSLEEP); } } static int mpr_user_reg_access(struct mpr_softc *sc, mpr_reg_access_t *data) { int status = 0; switch (data->Command) { /* * IO access is not supported. */ case REG_IO_READ: case REG_IO_WRITE: mpr_dprint(sc, MPR_USER, "IO access is not supported. " "Use memory access."); status = EINVAL; break; case REG_MEM_READ: data->RegData = mpr_regread(sc, data->RegOffset); break; case REG_MEM_WRITE: mpr_regwrite(sc, data->RegOffset, data->RegData); break; default: status = EINVAL; break; } return (status); } static int mpr_user_btdh(struct mpr_softc *sc, mpr_btdh_mapping_t *data) { uint8_t bt2dh = FALSE; uint8_t dh2bt = FALSE; uint16_t dev_handle, bus, target; bus = data->Bus; target = data->TargetID; dev_handle = data->DevHandle; /* * When DevHandle is 0xFFFF and Bus/Target are not 0xFFFF, use Bus/ * Target to get DevHandle. When Bus/Target are 0xFFFF and DevHandle is * not 0xFFFF, use DevHandle to get Bus/Target. Anything else is * invalid. */ if ((bus == 0xFFFF) && (target == 0xFFFF) && (dev_handle != 0xFFFF)) dh2bt = TRUE; if ((dev_handle == 0xFFFF) && (bus != 0xFFFF) && (target != 0xFFFF)) bt2dh = TRUE; if (!dh2bt && !bt2dh) return (EINVAL); /* * Only handle bus of 0. Make sure target is within range. */ if (bt2dh) { if (bus != 0) return (EINVAL); if (target > sc->max_devices) { mpr_dprint(sc, MPR_XINFO, "Target ID is out of range " "for Bus/Target to DevHandle mapping."); return (EINVAL); } dev_handle = sc->mapping_table[target].dev_handle; if (dev_handle) data->DevHandle = dev_handle; } else { bus = 0; target = mpr_mapping_get_tid_from_handle(sc, dev_handle); data->Bus = bus; data->TargetID = target; } return (0); } static int mpr_ioctl(struct cdev *dev, u_long cmd, void *arg, int flag, struct thread *td) { struct mpr_softc *sc; struct mpr_cfg_page_req *page_req; struct mpr_ext_cfg_page_req *ext_page_req; void *mpr_page; int error, msleep_ret; mpr_page = NULL; sc = dev->si_drv1; page_req = (void *)arg; ext_page_req = (void *)arg; switch (cmd) { case MPRIO_READ_CFG_HEADER: mpr_lock(sc); error = mpr_user_read_cfg_header(sc, page_req); mpr_unlock(sc); break; case MPRIO_READ_CFG_PAGE: mpr_page = malloc(page_req->len, M_MPRUSER, M_WAITOK | M_ZERO); error = copyin(page_req->buf, mpr_page, sizeof(MPI2_CONFIG_PAGE_HEADER)); if (error) break; mpr_lock(sc); error = mpr_user_read_cfg_page(sc, page_req, mpr_page); mpr_unlock(sc); if (error) break; error = copyout(mpr_page, page_req->buf, page_req->len); break; case MPRIO_READ_EXT_CFG_HEADER: mpr_lock(sc); error = mpr_user_read_extcfg_header(sc, ext_page_req); mpr_unlock(sc); break; case MPRIO_READ_EXT_CFG_PAGE: mpr_page = malloc(ext_page_req->len, M_MPRUSER, M_WAITOK | M_ZERO); error = copyin(ext_page_req->buf, mpr_page, sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); if (error) break; mpr_lock(sc); error = mpr_user_read_extcfg_page(sc, ext_page_req, mpr_page); mpr_unlock(sc); if (error) break; error = copyout(mpr_page, ext_page_req->buf, ext_page_req->len); break; case MPRIO_WRITE_CFG_PAGE: mpr_page = malloc(page_req->len, M_MPRUSER, M_WAITOK|M_ZERO); error = copyin(page_req->buf, mpr_page, page_req->len); if (error) break; mpr_lock(sc); error = mpr_user_write_cfg_page(sc, page_req, mpr_page); mpr_unlock(sc); break; case MPRIO_MPR_COMMAND: error = mpr_user_command(sc, (struct mpr_usr_command *)arg); break; case MPTIOCTL_PASS_THRU: /* * The user has requested to pass through a command to be * executed by the MPT firmware. Call our routine which does * this. Only allow one passthru IOCTL at one time. */ error = mpr_user_pass_thru(sc, (mpr_pass_thru_t *)arg); break; case MPTIOCTL_GET_ADAPTER_DATA: /* * The user has requested to read adapter data. Call our * routine which does this. */ error = 0; mpr_user_get_adapter_data(sc, (mpr_adapter_data_t *)arg); break; case MPTIOCTL_GET_PCI_INFO: /* * The user has requested to read pci info. Call * our routine which does this. */ mpr_lock(sc); error = 0; mpr_user_read_pci_info(sc, (mpr_pci_info_t *)arg); mpr_unlock(sc); break; case MPTIOCTL_RESET_ADAPTER: mpr_lock(sc); sc->port_enable_complete = 0; uint32_t reinit_start = time_uptime; error = mpr_reinit(sc); /* Sleep for 300 second. */ msleep_ret = msleep(&sc->port_enable_complete, &sc->mpr_mtx, PRIBIO, "mpr_porten", 300 * hz); mpr_unlock(sc); if (msleep_ret) printf("Port Enable did not complete after Diag " "Reset msleep error %d.\n", msleep_ret); else mpr_dprint(sc, MPR_USER, "Hard Reset with Port Enable " "completed in %d seconds.\n", (uint32_t)(time_uptime - reinit_start)); break; case MPTIOCTL_DIAG_ACTION: /* * The user has done a diag buffer action. Call our routine * which does this. Only allow one diag action at one time. */ mpr_lock(sc); error = mpr_user_diag_action(sc, (mpr_diag_action_t *)arg); mpr_unlock(sc); break; case MPTIOCTL_EVENT_QUERY: /* * The user has done an event query. Call our routine which does * this. */ error = 0; mpr_user_event_query(sc, (mpr_event_query_t *)arg); break; case MPTIOCTL_EVENT_ENABLE: /* * The user has done an event enable. Call our routine which * does this. */ error = 0; mpr_user_event_enable(sc, (mpr_event_enable_t *)arg); break; case MPTIOCTL_EVENT_REPORT: /* * The user has done an event report. Call our routine which * does this. */ error = mpr_user_event_report(sc, (mpr_event_report_t *)arg); break; case MPTIOCTL_REG_ACCESS: /* * The user has requested register access. Call our routine * which does this. */ mpr_lock(sc); error = mpr_user_reg_access(sc, (mpr_reg_access_t *)arg); mpr_unlock(sc); break; case MPTIOCTL_BTDH_MAPPING: /* * The user has requested to translate a bus/target to a * DevHandle or a DevHandle to a bus/target. Call our routine * which does this. */ error = mpr_user_btdh(sc, (mpr_btdh_mapping_t *)arg); break; default: error = ENOIOCTL; break; } if (mpr_page != NULL) free(mpr_page, M_MPRUSER); return (error); } #ifdef COMPAT_FREEBSD32 struct mpr_cfg_page_req32 { MPI2_CONFIG_PAGE_HEADER header; uint32_t page_address; uint32_t buf; int len; uint16_t ioc_status; }; struct mpr_ext_cfg_page_req32 { MPI2_CONFIG_EXTENDED_PAGE_HEADER header; uint32_t page_address; uint32_t buf; int len; uint16_t ioc_status; }; struct mpr_raid_action32 { uint8_t action; uint8_t volume_bus; uint8_t volume_id; uint8_t phys_disk_num; uint32_t action_data_word; uint32_t buf; int len; uint32_t volume_status; uint32_t action_data[4]; uint16_t action_status; uint16_t ioc_status; uint8_t write; }; struct mpr_usr_command32 { uint32_t req; uint32_t req_len; uint32_t rpl; uint32_t rpl_len; uint32_t buf; int len; uint32_t flags; }; #define MPRIO_READ_CFG_HEADER32 _IOWR('M', 200, struct mpr_cfg_page_req32) #define MPRIO_READ_CFG_PAGE32 _IOWR('M', 201, struct mpr_cfg_page_req32) #define MPRIO_READ_EXT_CFG_HEADER32 _IOWR('M', 202, struct mpr_ext_cfg_page_req32) #define MPRIO_READ_EXT_CFG_PAGE32 _IOWR('M', 203, struct mpr_ext_cfg_page_req32) #define MPRIO_WRITE_CFG_PAGE32 _IOWR('M', 204, struct mpr_cfg_page_req32) #define MPRIO_RAID_ACTION32 _IOWR('M', 205, struct mpr_raid_action32) #define MPRIO_MPR_COMMAND32 _IOWR('M', 210, struct mpr_usr_command32) static int mpr_ioctl32(struct cdev *dev, u_long cmd32, void *_arg, int flag, struct thread *td) { struct mpr_cfg_page_req32 *page32 = _arg; struct mpr_ext_cfg_page_req32 *ext32 = _arg; struct mpr_raid_action32 *raid32 = _arg; struct mpr_usr_command32 *user32 = _arg; union { struct mpr_cfg_page_req page; struct mpr_ext_cfg_page_req ext; struct mpr_raid_action raid; struct mpr_usr_command user; } arg; u_long cmd; int error; switch (cmd32) { case MPRIO_READ_CFG_HEADER32: case MPRIO_READ_CFG_PAGE32: case MPRIO_WRITE_CFG_PAGE32: if (cmd32 == MPRIO_READ_CFG_HEADER32) cmd = MPRIO_READ_CFG_HEADER; else if (cmd32 == MPRIO_READ_CFG_PAGE32) cmd = MPRIO_READ_CFG_PAGE; else cmd = MPRIO_WRITE_CFG_PAGE; CP(*page32, arg.page, header); CP(*page32, arg.page, page_address); PTRIN_CP(*page32, arg.page, buf); CP(*page32, arg.page, len); CP(*page32, arg.page, ioc_status); break; case MPRIO_READ_EXT_CFG_HEADER32: case MPRIO_READ_EXT_CFG_PAGE32: if (cmd32 == MPRIO_READ_EXT_CFG_HEADER32) cmd = MPRIO_READ_EXT_CFG_HEADER; else cmd = MPRIO_READ_EXT_CFG_PAGE; CP(*ext32, arg.ext, header); CP(*ext32, arg.ext, page_address); PTRIN_CP(*ext32, arg.ext, buf); CP(*ext32, arg.ext, len); CP(*ext32, arg.ext, ioc_status); break; case MPRIO_RAID_ACTION32: cmd = MPRIO_RAID_ACTION; CP(*raid32, arg.raid, action); CP(*raid32, arg.raid, volume_bus); CP(*raid32, arg.raid, volume_id); CP(*raid32, arg.raid, phys_disk_num); CP(*raid32, arg.raid, action_data_word); PTRIN_CP(*raid32, arg.raid, buf); CP(*raid32, arg.raid, len); CP(*raid32, arg.raid, volume_status); bcopy(raid32->action_data, arg.raid.action_data, sizeof arg.raid.action_data); CP(*raid32, arg.raid, ioc_status); CP(*raid32, arg.raid, write); break; case MPRIO_MPR_COMMAND32: cmd = MPRIO_MPR_COMMAND; PTRIN_CP(*user32, arg.user, req); CP(*user32, arg.user, req_len); PTRIN_CP(*user32, arg.user, rpl); CP(*user32, arg.user, rpl_len); PTRIN_CP(*user32, arg.user, buf); CP(*user32, arg.user, len); CP(*user32, arg.user, flags); break; default: return (ENOIOCTL); } error = mpr_ioctl(dev, cmd, &arg, flag, td); if (error == 0 && (cmd32 & IOC_OUT) != 0) { switch (cmd32) { case MPRIO_READ_CFG_HEADER32: case MPRIO_READ_CFG_PAGE32: case MPRIO_WRITE_CFG_PAGE32: CP(arg.page, *page32, header); CP(arg.page, *page32, page_address); PTROUT_CP(arg.page, *page32, buf); CP(arg.page, *page32, len); CP(arg.page, *page32, ioc_status); break; case MPRIO_READ_EXT_CFG_HEADER32: case MPRIO_READ_EXT_CFG_PAGE32: CP(arg.ext, *ext32, header); CP(arg.ext, *ext32, page_address); PTROUT_CP(arg.ext, *ext32, buf); CP(arg.ext, *ext32, len); CP(arg.ext, *ext32, ioc_status); break; case MPRIO_RAID_ACTION32: CP(arg.raid, *raid32, action); CP(arg.raid, *raid32, volume_bus); CP(arg.raid, *raid32, volume_id); CP(arg.raid, *raid32, phys_disk_num); CP(arg.raid, *raid32, action_data_word); PTROUT_CP(arg.raid, *raid32, buf); CP(arg.raid, *raid32, len); CP(arg.raid, *raid32, volume_status); bcopy(arg.raid.action_data, raid32->action_data, sizeof arg.raid.action_data); CP(arg.raid, *raid32, ioc_status); CP(arg.raid, *raid32, write); break; case MPRIO_MPR_COMMAND32: PTROUT_CP(arg.user, *user32, req); CP(arg.user, *user32, req_len); PTROUT_CP(arg.user, *user32, rpl); CP(arg.user, *user32, rpl_len); PTROUT_CP(arg.user, *user32, buf); CP(arg.user, *user32, len); CP(arg.user, *user32, flags); break; } } return (error); } #endif /* COMPAT_FREEBSD32 */ static int mpr_ioctl_devsw(struct cdev *dev, u_long com, caddr_t arg, int flag, struct thread *td) { #ifdef COMPAT_FREEBSD32 if (SV_CURPROC_FLAG(SV_ILP32)) return (mpr_ioctl32(dev, com, arg, flag, td)); #endif return (mpr_ioctl(dev, com, arg, flag, td)); } Index: stable/12/sys/dev/mps/mps_user.c =================================================================== --- stable/12/sys/dev/mps/mps_user.c (revision 360445) +++ stable/12/sys/dev/mps/mps_user.c (revision 360446) @@ -1,2527 +1,2518 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2008 Yahoo!, Inc. * All rights reserved. * Written by: John Baldwin * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Avago Technologies (LSI) MPT-Fusion Host Adapter FreeBSD userland interface */ /*- * Copyright (c) 2011-2015 LSI Corp. * Copyright (c) 2013-2015 Avago Technologies * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Avago Technologies (LSI) MPT-Fusion Host Adapter FreeBSD * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); /* TODO Move headers to mpsvar */ #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static d_open_t mps_open; static d_close_t mps_close; static d_ioctl_t mps_ioctl_devsw; static struct cdevsw mps_cdevsw = { .d_version = D_VERSION, .d_flags = 0, .d_open = mps_open, .d_close = mps_close, .d_ioctl = mps_ioctl_devsw, .d_name = "mps", }; typedef int (mps_user_f)(struct mps_command *, struct mps_usr_command *); static mps_user_f mpi_pre_ioc_facts; static mps_user_f mpi_pre_port_facts; static mps_user_f mpi_pre_fw_download; static mps_user_f mpi_pre_fw_upload; static mps_user_f mpi_pre_sata_passthrough; static mps_user_f mpi_pre_smp_passthrough; static mps_user_f mpi_pre_config; static mps_user_f mpi_pre_sas_io_unit_control; static int mps_user_read_cfg_header(struct mps_softc *, struct mps_cfg_page_req *); static int mps_user_read_cfg_page(struct mps_softc *, struct mps_cfg_page_req *, void *); static int mps_user_read_extcfg_header(struct mps_softc *, struct mps_ext_cfg_page_req *); static int mps_user_read_extcfg_page(struct mps_softc *, struct mps_ext_cfg_page_req *, void *); static int mps_user_write_cfg_page(struct mps_softc *, struct mps_cfg_page_req *, void *); static int mps_user_setup_request(struct mps_command *, struct mps_usr_command *); static int mps_user_command(struct mps_softc *, struct mps_usr_command *); static int mps_user_pass_thru(struct mps_softc *sc, mps_pass_thru_t *data); static void mps_user_get_adapter_data(struct mps_softc *sc, mps_adapter_data_t *data); static void mps_user_read_pci_info(struct mps_softc *sc, mps_pci_info_t *data); static uint8_t mps_get_fw_diag_buffer_number(struct mps_softc *sc, uint32_t unique_id); static int mps_post_fw_diag_buffer(struct mps_softc *sc, mps_fw_diagnostic_buffer_t *pBuffer, uint32_t *return_code); static int mps_release_fw_diag_buffer(struct mps_softc *sc, mps_fw_diagnostic_buffer_t *pBuffer, uint32_t *return_code, uint32_t diag_type); static int mps_diag_register(struct mps_softc *sc, mps_fw_diag_register_t *diag_register, uint32_t *return_code); static int mps_diag_unregister(struct mps_softc *sc, mps_fw_diag_unregister_t *diag_unregister, uint32_t *return_code); static int mps_diag_query(struct mps_softc *sc, mps_fw_diag_query_t *diag_query, uint32_t *return_code); static int mps_diag_read_buffer(struct mps_softc *sc, mps_diag_read_buffer_t *diag_read_buffer, uint8_t *ioctl_buf, uint32_t *return_code); static int mps_diag_release(struct mps_softc *sc, mps_fw_diag_release_t *diag_release, uint32_t *return_code); static int mps_do_diag_action(struct mps_softc *sc, uint32_t action, uint8_t *diag_action, uint32_t length, uint32_t *return_code); static int mps_user_diag_action(struct mps_softc *sc, mps_diag_action_t *data); static void mps_user_event_query(struct mps_softc *sc, mps_event_query_t *data); static void mps_user_event_enable(struct mps_softc *sc, mps_event_enable_t *data); static int mps_user_event_report(struct mps_softc *sc, mps_event_report_t *data); static int mps_user_reg_access(struct mps_softc *sc, mps_reg_access_t *data); static int mps_user_btdh(struct mps_softc *sc, mps_btdh_mapping_t *data); MALLOC_DEFINE(M_MPSUSER, "mps_user", "Buffers for mps(4) ioctls"); - -/* Macros from compat/freebsd32/freebsd32.h */ -#define PTRIN(v) (void *)(uintptr_t)(v) -#define PTROUT(v) (uint32_t)(uintptr_t)(v) - -#define CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0) -#define PTRIN_CP(src,dst,fld) \ - do { (dst).fld = PTRIN((src).fld); } while (0) -#define PTROUT_CP(src,dst,fld) \ - do { (dst).fld = PTROUT((src).fld); } while (0) int mps_attach_user(struct mps_softc *sc) { int unit; unit = device_get_unit(sc->mps_dev); sc->mps_cdev = make_dev(&mps_cdevsw, unit, UID_ROOT, GID_OPERATOR, 0640, "mps%d", unit); if (sc->mps_cdev == NULL) { return (ENOMEM); } sc->mps_cdev->si_drv1 = sc; return (0); } void mps_detach_user(struct mps_softc *sc) { /* XXX: do a purge of pending requests? */ if (sc->mps_cdev != NULL) destroy_dev(sc->mps_cdev); } static int mps_open(struct cdev *dev, int flags, int fmt, struct thread *td) { return (0); } static int mps_close(struct cdev *dev, int flags, int fmt, struct thread *td) { return (0); } static int mps_user_read_cfg_header(struct mps_softc *sc, struct mps_cfg_page_req *page_req) { MPI2_CONFIG_PAGE_HEADER *hdr; struct mps_config_params params; int error; hdr = ¶ms.hdr.Struct; params.action = MPI2_CONFIG_ACTION_PAGE_HEADER; params.page_address = le32toh(page_req->page_address); hdr->PageVersion = 0; hdr->PageLength = 0; hdr->PageNumber = page_req->header.PageNumber; hdr->PageType = page_req->header.PageType; params.buffer = NULL; params.length = 0; params.callback = NULL; if ((error = mps_read_config_page(sc, ¶ms)) != 0) { /* * Leave the request. Without resetting the chip, it's * still owned by it and we'll just get into trouble * freeing it now. Mark it as abandoned so that if it * shows up later it can be freed. */ mps_printf(sc, "read_cfg_header timed out\n"); return (ETIMEDOUT); } page_req->ioc_status = htole16(params.status); if ((page_req->ioc_status & MPI2_IOCSTATUS_MASK) == MPI2_IOCSTATUS_SUCCESS) { bcopy(hdr, &page_req->header, sizeof(page_req->header)); } return (0); } static int mps_user_read_cfg_page(struct mps_softc *sc, struct mps_cfg_page_req *page_req, void *buf) { MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr; struct mps_config_params params; int error; reqhdr = buf; hdr = ¶ms.hdr.Struct; hdr->PageVersion = reqhdr->PageVersion; hdr->PageLength = reqhdr->PageLength; hdr->PageNumber = reqhdr->PageNumber; hdr->PageType = reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK; params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; params.page_address = le32toh(page_req->page_address); params.buffer = buf; params.length = le32toh(page_req->len); params.callback = NULL; if ((error = mps_read_config_page(sc, ¶ms)) != 0) { mps_printf(sc, "mps_user_read_cfg_page timed out\n"); return (ETIMEDOUT); } page_req->ioc_status = htole16(params.status); return (0); } static int mps_user_read_extcfg_header(struct mps_softc *sc, struct mps_ext_cfg_page_req *ext_page_req) { MPI2_CONFIG_EXTENDED_PAGE_HEADER *hdr; struct mps_config_params params; int error; hdr = ¶ms.hdr.Ext; params.action = MPI2_CONFIG_ACTION_PAGE_HEADER; hdr->PageVersion = ext_page_req->header.PageVersion; hdr->PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; hdr->ExtPageLength = 0; hdr->PageNumber = ext_page_req->header.PageNumber; hdr->ExtPageType = ext_page_req->header.ExtPageType; params.page_address = le32toh(ext_page_req->page_address); params.buffer = NULL; params.length = 0; params.callback = NULL; if ((error = mps_read_config_page(sc, ¶ms)) != 0) { /* * Leave the request. Without resetting the chip, it's * still owned by it and we'll just get into trouble * freeing it now. Mark it as abandoned so that if it * shows up later it can be freed. */ mps_printf(sc, "mps_user_read_extcfg_header timed out\n"); return (ETIMEDOUT); } ext_page_req->ioc_status = htole16(params.status); if ((ext_page_req->ioc_status & MPI2_IOCSTATUS_MASK) == MPI2_IOCSTATUS_SUCCESS) { ext_page_req->header.PageVersion = hdr->PageVersion; ext_page_req->header.PageNumber = hdr->PageNumber; ext_page_req->header.PageType = hdr->PageType; ext_page_req->header.ExtPageLength = hdr->ExtPageLength; ext_page_req->header.ExtPageType = hdr->ExtPageType; } return (0); } static int mps_user_read_extcfg_page(struct mps_softc *sc, struct mps_ext_cfg_page_req *ext_page_req, void *buf) { MPI2_CONFIG_EXTENDED_PAGE_HEADER *reqhdr, *hdr; struct mps_config_params params; int error; reqhdr = buf; hdr = ¶ms.hdr.Ext; params.action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; params.page_address = le32toh(ext_page_req->page_address); hdr->PageVersion = reqhdr->PageVersion; hdr->PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; hdr->PageNumber = reqhdr->PageNumber; hdr->ExtPageType = reqhdr->ExtPageType; hdr->ExtPageLength = reqhdr->ExtPageLength; params.buffer = buf; params.length = le32toh(ext_page_req->len); params.callback = NULL; if ((error = mps_read_config_page(sc, ¶ms)) != 0) { mps_printf(sc, "mps_user_read_extcfg_page timed out\n"); return (ETIMEDOUT); } ext_page_req->ioc_status = htole16(params.status); return (0); } static int mps_user_write_cfg_page(struct mps_softc *sc, struct mps_cfg_page_req *page_req, void *buf) { MPI2_CONFIG_PAGE_HEADER *reqhdr, *hdr; struct mps_config_params params; u_int hdr_attr; int error; reqhdr = buf; hdr = ¶ms.hdr.Struct; hdr_attr = reqhdr->PageType & MPI2_CONFIG_PAGEATTR_MASK; if (hdr_attr != MPI2_CONFIG_PAGEATTR_CHANGEABLE && hdr_attr != MPI2_CONFIG_PAGEATTR_PERSISTENT) { mps_printf(sc, "page type 0x%x not changeable\n", reqhdr->PageType & MPI2_CONFIG_PAGETYPE_MASK); return (EINVAL); } /* * There isn't any point in restoring stripped out attributes * if you then mask them going down to issue the request. */ hdr->PageVersion = reqhdr->PageVersion; hdr->PageLength = reqhdr->PageLength; hdr->PageNumber = reqhdr->PageNumber; hdr->PageType = reqhdr->PageType; params.action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; params.page_address = le32toh(page_req->page_address); params.buffer = buf; params.length = le32toh(page_req->len); params.callback = NULL; if ((error = mps_write_config_page(sc, ¶ms)) != 0) { mps_printf(sc, "mps_write_cfg_page timed out\n"); return (ETIMEDOUT); } page_req->ioc_status = htole16(params.status); return (0); } void mpi_init_sge(struct mps_command *cm, void *req, void *sge) { int off, space; space = (int)cm->cm_sc->reqframesz; off = (uintptr_t)sge - (uintptr_t)req; KASSERT(off < space, ("bad pointers %p %p, off %d, space %d", req, sge, off, space)); cm->cm_sge = sge; cm->cm_sglsize = space - off; } /* * Prepare the mps_command for an IOC_FACTS request. */ static int mpi_pre_ioc_facts(struct mps_command *cm, struct mps_usr_command *cmd) { MPI2_IOC_FACTS_REQUEST *req = (void *)cm->cm_req; MPI2_IOC_FACTS_REPLY *rpl; if (cmd->req_len != sizeof *req) return (EINVAL); if (cmd->rpl_len != sizeof *rpl) return (EINVAL); cm->cm_sge = NULL; cm->cm_sglsize = 0; return (0); } /* * Prepare the mps_command for a PORT_FACTS request. */ static int mpi_pre_port_facts(struct mps_command *cm, struct mps_usr_command *cmd) { MPI2_PORT_FACTS_REQUEST *req = (void *)cm->cm_req; MPI2_PORT_FACTS_REPLY *rpl; if (cmd->req_len != sizeof *req) return (EINVAL); if (cmd->rpl_len != sizeof *rpl) return (EINVAL); cm->cm_sge = NULL; cm->cm_sglsize = 0; return (0); } /* * Prepare the mps_command for a FW_DOWNLOAD request. */ static int mpi_pre_fw_download(struct mps_command *cm, struct mps_usr_command *cmd) { MPI2_FW_DOWNLOAD_REQUEST *req = (void *)cm->cm_req; MPI2_FW_DOWNLOAD_REPLY *rpl; MPI2_FW_DOWNLOAD_TCSGE tc; int error; /* * This code assumes there is room in the request's SGL for * the TransactionContext plus at least a SGL chain element. */ CTASSERT(sizeof req->SGL >= sizeof tc + MPS_SGC_SIZE); if (cmd->req_len != sizeof *req) return (EINVAL); if (cmd->rpl_len != sizeof *rpl) return (EINVAL); if (cmd->len == 0) return (EINVAL); error = copyin(cmd->buf, cm->cm_data, cmd->len); if (error != 0) return (error); mpi_init_sge(cm, req, &req->SGL); bzero(&tc, sizeof tc); /* * For now, the F/W image must be provided in a single request. */ if ((req->MsgFlags & MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT) == 0) return (EINVAL); if (req->TotalImageSize != cmd->len) return (EINVAL); /* * The value of the first two elements is specified in the * Fusion-MPT Message Passing Interface document. */ tc.ContextSize = 0; tc.DetailsLength = 12; tc.ImageOffset = 0; tc.ImageSize = cmd->len; cm->cm_flags |= MPS_CM_FLAGS_DATAOUT; return (mps_push_sge(cm, &tc, sizeof tc, 0)); } /* * Prepare the mps_command for a FW_UPLOAD request. */ static int mpi_pre_fw_upload(struct mps_command *cm, struct mps_usr_command *cmd) { MPI2_FW_UPLOAD_REQUEST *req = (void *)cm->cm_req; MPI2_FW_UPLOAD_REPLY *rpl; MPI2_FW_UPLOAD_TCSGE tc; /* * This code assumes there is room in the request's SGL for * the TransactionContext plus at least a SGL chain element. */ CTASSERT(sizeof req->SGL >= sizeof tc + MPS_SGC_SIZE); if (cmd->req_len != sizeof *req) return (EINVAL); if (cmd->rpl_len != sizeof *rpl) return (EINVAL); mpi_init_sge(cm, req, &req->SGL); bzero(&tc, sizeof tc); /* * The value of the first two elements is specified in the * Fusion-MPT Message Passing Interface document. */ tc.ContextSize = 0; tc.DetailsLength = 12; /* * XXX Is there any reason to fetch a partial image? I.e. to * set ImageOffset to something other than 0? */ tc.ImageOffset = 0; tc.ImageSize = cmd->len; cm->cm_flags |= MPS_CM_FLAGS_DATAIN; return (mps_push_sge(cm, &tc, sizeof tc, 0)); } /* * Prepare the mps_command for a SATA_PASSTHROUGH request. */ static int mpi_pre_sata_passthrough(struct mps_command *cm, struct mps_usr_command *cmd) { MPI2_SATA_PASSTHROUGH_REQUEST *req = (void *)cm->cm_req; MPI2_SATA_PASSTHROUGH_REPLY *rpl; if (cmd->req_len != sizeof *req) return (EINVAL); if (cmd->rpl_len != sizeof *rpl) return (EINVAL); mpi_init_sge(cm, req, &req->SGL); return (0); } /* * Prepare the mps_command for a SMP_PASSTHROUGH request. */ static int mpi_pre_smp_passthrough(struct mps_command *cm, struct mps_usr_command *cmd) { MPI2_SMP_PASSTHROUGH_REQUEST *req = (void *)cm->cm_req; MPI2_SMP_PASSTHROUGH_REPLY *rpl; if (cmd->req_len != sizeof *req) return (EINVAL); if (cmd->rpl_len != sizeof *rpl) return (EINVAL); mpi_init_sge(cm, req, &req->SGL); return (0); } /* * Prepare the mps_command for a CONFIG request. */ static int mpi_pre_config(struct mps_command *cm, struct mps_usr_command *cmd) { MPI2_CONFIG_REQUEST *req = (void *)cm->cm_req; MPI2_CONFIG_REPLY *rpl; if (cmd->req_len != sizeof *req) return (EINVAL); if (cmd->rpl_len != sizeof *rpl) return (EINVAL); mpi_init_sge(cm, req, &req->PageBufferSGE); return (0); } /* * Prepare the mps_command for a SAS_IO_UNIT_CONTROL request. */ static int mpi_pre_sas_io_unit_control(struct mps_command *cm, struct mps_usr_command *cmd) { cm->cm_sge = NULL; cm->cm_sglsize = 0; return (0); } /* * A set of functions to prepare an mps_command for the various * supported requests. */ struct mps_user_func { U8 Function; mps_user_f *f_pre; } mps_user_func_list[] = { { MPI2_FUNCTION_IOC_FACTS, mpi_pre_ioc_facts }, { MPI2_FUNCTION_PORT_FACTS, mpi_pre_port_facts }, { MPI2_FUNCTION_FW_DOWNLOAD, mpi_pre_fw_download }, { MPI2_FUNCTION_FW_UPLOAD, mpi_pre_fw_upload }, { MPI2_FUNCTION_SATA_PASSTHROUGH, mpi_pre_sata_passthrough }, { MPI2_FUNCTION_SMP_PASSTHROUGH, mpi_pre_smp_passthrough}, { MPI2_FUNCTION_CONFIG, mpi_pre_config}, { MPI2_FUNCTION_SAS_IO_UNIT_CONTROL, mpi_pre_sas_io_unit_control }, { 0xFF, NULL } /* list end */ }; static int mps_user_setup_request(struct mps_command *cm, struct mps_usr_command *cmd) { MPI2_REQUEST_HEADER *hdr = (MPI2_REQUEST_HEADER *)cm->cm_req; struct mps_user_func *f; for (f = mps_user_func_list; f->f_pre != NULL; f++) { if (hdr->Function == f->Function) return (f->f_pre(cm, cmd)); } return (EINVAL); } static int mps_user_command(struct mps_softc *sc, struct mps_usr_command *cmd) { MPI2_REQUEST_HEADER *hdr; MPI2_DEFAULT_REPLY *rpl; void *buf = NULL; struct mps_command *cm = NULL; int err = 0; int sz; mps_lock(sc); cm = mps_alloc_command(sc); if (cm == NULL) { mps_printf(sc, "%s: no mps requests\n", __func__); err = ENOMEM; goto RetFree; } mps_unlock(sc); hdr = (MPI2_REQUEST_HEADER *)cm->cm_req; mps_dprint(sc, MPS_USER, "%s: req %p %d rpl %p %d\n", __func__, cmd->req, cmd->req_len, cmd->rpl, cmd->rpl_len); if (cmd->req_len > (int)sc->reqframesz) { err = EINVAL; goto RetFreeUnlocked; } err = copyin(cmd->req, hdr, cmd->req_len); if (err != 0) goto RetFreeUnlocked; mps_dprint(sc, MPS_USER, "%s: Function %02X MsgFlags %02X\n", __func__, hdr->Function, hdr->MsgFlags); if (cmd->len > 0) { buf = malloc(cmd->len, M_MPSUSER, M_WAITOK|M_ZERO); cm->cm_data = buf; cm->cm_length = cmd->len; } else { cm->cm_data = NULL; cm->cm_length = 0; } cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE; cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; err = mps_user_setup_request(cm, cmd); if (err == EINVAL) { mps_printf(sc, "%s: unsupported parameter or unsupported " "function in request (function = 0x%X)\n", __func__, hdr->Function); } if (err != 0) goto RetFreeUnlocked; mps_lock(sc); err = mps_wait_command(sc, &cm, 60, CAN_SLEEP); if (err || (cm == NULL)) { mps_printf(sc, "%s: invalid request: error %d\n", __func__, err); goto RetFree; } rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply; if (rpl != NULL) sz = rpl->MsgLength * 4; else sz = 0; if (sz > cmd->rpl_len) { mps_printf(sc, "%s: user reply buffer (%d) smaller than " "returned buffer (%d)\n", __func__, cmd->rpl_len, sz); sz = cmd->rpl_len; } mps_unlock(sc); copyout(rpl, cmd->rpl, sz); if (buf != NULL) copyout(buf, cmd->buf, cmd->len); mps_dprint(sc, MPS_USER, "%s: reply size %d\n", __func__, sz); RetFreeUnlocked: mps_lock(sc); RetFree: if (cm != NULL) mps_free_command(sc, cm); mps_unlock(sc); if (buf != NULL) free(buf, M_MPSUSER); return (err); } static int mps_user_pass_thru(struct mps_softc *sc, mps_pass_thru_t *data) { MPI2_REQUEST_HEADER *hdr, tmphdr; MPI2_DEFAULT_REPLY *rpl = NULL; struct mps_command *cm = NULL; int err = 0, dir = 0, sz; uint8_t function = 0; u_int sense_len; struct mpssas_target *targ = NULL; /* * Only allow one passthru command at a time. Use the MPS_FLAGS_BUSY * bit to denote that a passthru is being processed. */ mps_lock(sc); if (sc->mps_flags & MPS_FLAGS_BUSY) { mps_dprint(sc, MPS_USER, "%s: Only one passthru command " "allowed at a single time.", __func__); mps_unlock(sc); return (EBUSY); } sc->mps_flags |= MPS_FLAGS_BUSY; mps_unlock(sc); /* * Do some validation on data direction. Valid cases are: * 1) DataSize is 0 and direction is NONE * 2) DataSize is non-zero and one of: * a) direction is READ or * b) direction is WRITE or * c) direction is BOTH and DataOutSize is non-zero * If valid and the direction is BOTH, change the direction to READ. * if valid and the direction is not BOTH, make sure DataOutSize is 0. */ if (((data->DataSize == 0) && (data->DataDirection == MPS_PASS_THRU_DIRECTION_NONE)) || ((data->DataSize != 0) && ((data->DataDirection == MPS_PASS_THRU_DIRECTION_READ) || (data->DataDirection == MPS_PASS_THRU_DIRECTION_WRITE) || ((data->DataDirection == MPS_PASS_THRU_DIRECTION_BOTH) && (data->DataOutSize != 0))))) { if (data->DataDirection == MPS_PASS_THRU_DIRECTION_BOTH) data->DataDirection = MPS_PASS_THRU_DIRECTION_READ; else data->DataOutSize = 0; } else { err = EINVAL; goto RetFreeUnlocked; } mps_dprint(sc, MPS_USER, "%s: req 0x%jx %d rpl 0x%jx %d " "data in 0x%jx %d data out 0x%jx %d data dir %d\n", __func__, data->PtrRequest, data->RequestSize, data->PtrReply, data->ReplySize, data->PtrData, data->DataSize, data->PtrDataOut, data->DataOutSize, data->DataDirection); /* * copy in the header so we know what we're dealing with before we * commit to allocating a command for it. */ err = copyin(PTRIN(data->PtrRequest), &tmphdr, data->RequestSize); if (err != 0) goto RetFreeUnlocked; if (data->RequestSize > (int)sc->reqframesz) { err = EINVAL; goto RetFreeUnlocked; } function = tmphdr.Function; mps_dprint(sc, MPS_USER, "%s: Function %02X MsgFlags %02X\n", __func__, function, tmphdr.MsgFlags); /* * Handle a passthru TM request. */ if (function == MPI2_FUNCTION_SCSI_TASK_MGMT) { MPI2_SCSI_TASK_MANAGE_REQUEST *task; mps_lock(sc); cm = mpssas_alloc_tm(sc); if (cm == NULL) { err = EINVAL; goto Ret; } /* Copy the header in. Only a small fixup is needed. */ task = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req; bcopy(&tmphdr, task, data->RequestSize); task->TaskMID = cm->cm_desc.Default.SMID; cm->cm_data = NULL; cm->cm_complete = NULL; cm->cm_complete_data = NULL; targ = mpssas_find_target_by_handle(sc->sassc, 0, task->DevHandle); if (targ == NULL) { mps_dprint(sc, MPS_INFO, "%s %d : invalid handle for requested TM 0x%x \n", __func__, __LINE__, task->DevHandle); err = 1; } else { mpssas_prepare_for_tm(sc, cm, targ, CAM_LUN_WILDCARD); err = mps_wait_command(sc, &cm, 30, CAN_SLEEP); } if (err != 0) { err = EIO; mps_dprint(sc, MPS_FAULT, "%s: task management failed", __func__); } /* * Copy the reply data and sense data to user space. */ if ((cm != NULL) && (cm->cm_reply != NULL)) { rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply; sz = rpl->MsgLength * 4; if (sz > data->ReplySize) { mps_printf(sc, "%s: user reply buffer (%d) " "smaller than returned buffer (%d)\n", __func__, data->ReplySize, sz); } mps_unlock(sc); copyout(cm->cm_reply, PTRIN(data->PtrReply), data->ReplySize); mps_lock(sc); } mpssas_free_tm(sc, cm); goto Ret; } mps_lock(sc); cm = mps_alloc_command(sc); if (cm == NULL) { mps_printf(sc, "%s: no mps requests\n", __func__); err = ENOMEM; goto Ret; } mps_unlock(sc); hdr = (MPI2_REQUEST_HEADER *)cm->cm_req; bcopy(&tmphdr, hdr, data->RequestSize); /* * Do some checking to make sure the IOCTL request contains a valid * request. Then set the SGL info. */ mpi_init_sge(cm, hdr, (void *)((uint8_t *)hdr + data->RequestSize)); /* * Set up for read, write or both. From check above, DataOutSize will * be 0 if direction is READ or WRITE, but it will have some non-zero * value if the direction is BOTH. So, just use the biggest size to get * the cm_data buffer size. If direction is BOTH, 2 SGLs need to be set * up; the first is for the request and the second will contain the * response data. cm_out_len needs to be set here and this will be used * when the SGLs are set up. */ cm->cm_data = NULL; cm->cm_length = MAX(data->DataSize, data->DataOutSize); cm->cm_out_len = data->DataOutSize; cm->cm_flags = 0; if (cm->cm_length != 0) { cm->cm_data = malloc(cm->cm_length, M_MPSUSER, M_WAITOK | M_ZERO); cm->cm_flags = MPS_CM_FLAGS_DATAIN; if (data->DataOutSize) { cm->cm_flags |= MPS_CM_FLAGS_DATAOUT; err = copyin(PTRIN(data->PtrDataOut), cm->cm_data, data->DataOutSize); } else if (data->DataDirection == MPS_PASS_THRU_DIRECTION_WRITE) { cm->cm_flags = MPS_CM_FLAGS_DATAOUT; err = copyin(PTRIN(data->PtrData), cm->cm_data, data->DataSize); } if (err != 0) mps_dprint(sc, MPS_FAULT, "%s: failed to copy " "IOCTL data from user space\n", __func__); } cm->cm_flags |= MPS_CM_FLAGS_SGE_SIMPLE; cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; /* * Set up Sense buffer and SGL offset for IO passthru. SCSI IO request * uses SCSI IO descriptor. */ if ((function == MPI2_FUNCTION_SCSI_IO_REQUEST) || (function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { MPI2_SCSI_IO_REQUEST *scsi_io_req; scsi_io_req = (MPI2_SCSI_IO_REQUEST *)hdr; /* * Put SGE for data and data_out buffer at the end of * scsi_io_request message header (64 bytes in total). * Following above SGEs, the residual space will be used by * sense data. */ scsi_io_req->SenseBufferLength = (uint8_t)(data->RequestSize - 64); scsi_io_req->SenseBufferLowAddress = htole32(cm->cm_sense_busaddr); /* * Set SGLOffset0 value. This is the number of dwords that SGL * is offset from the beginning of MPI2_SCSI_IO_REQUEST struct. */ scsi_io_req->SGLOffset0 = 24; /* * Setup descriptor info. RAID passthrough must use the * default request descriptor which is already set, so if this * is a SCSI IO request, change the descriptor to SCSI IO. * Also, if this is a SCSI IO request, handle the reply in the * mpssas_scsio_complete function. */ if (function == MPI2_FUNCTION_SCSI_IO_REQUEST) { cm->cm_desc.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; cm->cm_desc.SCSIIO.DevHandle = scsi_io_req->DevHandle; /* * Make sure the DevHandle is not 0 because this is a * likely error. */ if (scsi_io_req->DevHandle == 0) { err = EINVAL; goto RetFreeUnlocked; } } } mps_lock(sc); err = mps_wait_command(sc, &cm, 30, CAN_SLEEP); if (err || (cm == NULL)) { mps_printf(sc, "%s: invalid request: error %d\n", __func__, err); mps_unlock(sc); goto RetFreeUnlocked; } /* * Sync the DMA data, if any. Then copy the data to user space. */ if (cm->cm_data != NULL) { if (cm->cm_flags & MPS_CM_FLAGS_DATAIN) dir = BUS_DMASYNC_POSTREAD; else if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT) dir = BUS_DMASYNC_POSTWRITE; bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, dir); bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap); if (cm->cm_flags & MPS_CM_FLAGS_DATAIN) { mps_unlock(sc); err = copyout(cm->cm_data, PTRIN(data->PtrData), data->DataSize); mps_lock(sc); if (err != 0) mps_dprint(sc, MPS_FAULT, "%s: failed to copy " "IOCTL data to user space\n", __func__); } } /* * Copy the reply data and sense data to user space. */ if (cm->cm_reply != NULL) { rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply; sz = rpl->MsgLength * 4; if (sz > data->ReplySize) { mps_printf(sc, "%s: user reply buffer (%d) smaller " "than returned buffer (%d)\n", __func__, data->ReplySize, sz); } mps_unlock(sc); copyout(cm->cm_reply, PTRIN(data->PtrReply), data->ReplySize); mps_lock(sc); if ((function == MPI2_FUNCTION_SCSI_IO_REQUEST) || (function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { if (((MPI2_SCSI_IO_REPLY *)rpl)->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_VALID) { sense_len = MIN((le32toh(((MPI2_SCSI_IO_REPLY *)rpl)->SenseCount)), sizeof(struct scsi_sense_data)); mps_unlock(sc); copyout(cm->cm_sense, cm->cm_req + 64, sense_len); mps_lock(sc); } } } mps_unlock(sc); RetFreeUnlocked: mps_lock(sc); if (cm != NULL) { if (cm->cm_data) free(cm->cm_data, M_MPSUSER); mps_free_command(sc, cm); } Ret: sc->mps_flags &= ~MPS_FLAGS_BUSY; mps_unlock(sc); return (err); } static void mps_user_get_adapter_data(struct mps_softc *sc, mps_adapter_data_t *data) { Mpi2ConfigReply_t mpi_reply; Mpi2BiosPage3_t config_page; /* * Use the PCI interface functions to get the Bus, Device, and Function * information. */ data->PciInformation.u.bits.BusNumber = pci_get_bus(sc->mps_dev); data->PciInformation.u.bits.DeviceNumber = pci_get_slot(sc->mps_dev); data->PciInformation.u.bits.FunctionNumber = pci_get_function(sc->mps_dev); /* * Get the FW version that should already be saved in IOC Facts. */ data->MpiFirmwareVersion = sc->facts->FWVersion.Word; /* * General device info. */ data->AdapterType = MPSIOCTL_ADAPTER_TYPE_SAS2; if (sc->mps_flags & MPS_FLAGS_WD_AVAILABLE) data->AdapterType = MPSIOCTL_ADAPTER_TYPE_SAS2_SSS6200; data->PCIDeviceHwId = pci_get_device(sc->mps_dev); data->PCIDeviceHwRev = pci_read_config(sc->mps_dev, PCIR_REVID, 1); data->SubSystemId = pci_get_subdevice(sc->mps_dev); data->SubsystemVendorId = pci_get_subvendor(sc->mps_dev); /* * Get the driver version. */ strcpy((char *)&data->DriverVersion[0], MPS_DRIVER_VERSION); /* * Need to get BIOS Config Page 3 for the BIOS Version. */ data->BiosVersion = 0; mps_lock(sc); if (mps_config_get_bios_pg3(sc, &mpi_reply, &config_page)) printf("%s: Error while retrieving BIOS Version\n", __func__); else data->BiosVersion = config_page.BiosVersion; mps_unlock(sc); } static void mps_user_read_pci_info(struct mps_softc *sc, mps_pci_info_t *data) { int i; /* * Use the PCI interface functions to get the Bus, Device, and Function * information. */ data->BusNumber = pci_get_bus(sc->mps_dev); data->DeviceNumber = pci_get_slot(sc->mps_dev); data->FunctionNumber = pci_get_function(sc->mps_dev); /* * Now get the interrupt vector and the pci header. The vector can * only be 0 right now. The header is the first 256 bytes of config * space. */ data->InterruptVector = 0; for (i = 0; i < sizeof (data->PciHeader); i++) { data->PciHeader[i] = pci_read_config(sc->mps_dev, i, 1); } } static uint8_t mps_get_fw_diag_buffer_number(struct mps_softc *sc, uint32_t unique_id) { uint8_t index; for (index = 0; index < MPI2_DIAG_BUF_TYPE_COUNT; index++) { if (sc->fw_diag_buffer_list[index].unique_id == unique_id) { return (index); } } return (MPS_FW_DIAGNOSTIC_UID_NOT_FOUND); } static int mps_post_fw_diag_buffer(struct mps_softc *sc, mps_fw_diagnostic_buffer_t *pBuffer, uint32_t *return_code) { MPI2_DIAG_BUFFER_POST_REQUEST *req; MPI2_DIAG_BUFFER_POST_REPLY *reply = NULL; struct mps_command *cm = NULL; int i, status; /* * If buffer is not enabled, just leave. */ *return_code = MPS_FW_DIAG_ERROR_POST_FAILED; if (!pBuffer->enabled) { return (MPS_DIAG_FAILURE); } /* * Clear some flags initially. */ pBuffer->force_release = FALSE; pBuffer->valid_data = FALSE; pBuffer->owned_by_firmware = FALSE; /* * Get a command. */ cm = mps_alloc_command(sc); if (cm == NULL) { mps_printf(sc, "%s: no mps requests\n", __func__); return (MPS_DIAG_FAILURE); } /* * Build the request for releasing the FW Diag Buffer and send it. */ req = (MPI2_DIAG_BUFFER_POST_REQUEST *)cm->cm_req; req->Function = MPI2_FUNCTION_DIAG_BUFFER_POST; req->BufferType = pBuffer->buffer_type; req->ExtendedType = pBuffer->extended_type; req->BufferLength = pBuffer->size; for (i = 0; i < (sizeof(req->ProductSpecific) / 4); i++) req->ProductSpecific[i] = pBuffer->product_specific[i]; mps_from_u64(sc->fw_diag_busaddr, &req->BufferAddress); cm->cm_data = NULL; cm->cm_length = 0; cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; cm->cm_complete_data = NULL; /* * Send command synchronously. */ status = mps_wait_command(sc, &cm, 30, CAN_SLEEP); if (status || (cm == NULL)) { mps_printf(sc, "%s: invalid request: error %d\n", __func__, status); status = MPS_DIAG_FAILURE; goto done; } /* * Process POST reply. */ reply = (MPI2_DIAG_BUFFER_POST_REPLY *)cm->cm_reply; if (reply == NULL) { mps_printf(sc, "%s: reply is NULL, probably due to " "reinitialization\n", __func__); status = MPS_DIAG_FAILURE; goto done; } if ((le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS) { status = MPS_DIAG_FAILURE; mps_dprint(sc, MPS_FAULT, "%s: post of FW Diag Buffer failed " "with IOCStatus = 0x%x, IOCLogInfo = 0x%x and " "TransferLength = 0x%x\n", __func__, le16toh(reply->IOCStatus), le32toh(reply->IOCLogInfo), le32toh(reply->TransferLength)); goto done; } /* * Post was successful. */ pBuffer->valid_data = TRUE; pBuffer->owned_by_firmware = TRUE; *return_code = MPS_FW_DIAG_ERROR_SUCCESS; status = MPS_DIAG_SUCCESS; done: if (cm != NULL) mps_free_command(sc, cm); return (status); } static int mps_release_fw_diag_buffer(struct mps_softc *sc, mps_fw_diagnostic_buffer_t *pBuffer, uint32_t *return_code, uint32_t diag_type) { MPI2_DIAG_RELEASE_REQUEST *req; MPI2_DIAG_RELEASE_REPLY *reply = NULL; struct mps_command *cm = NULL; int status; /* * If buffer is not enabled, just leave. */ *return_code = MPS_FW_DIAG_ERROR_RELEASE_FAILED; if (!pBuffer->enabled) { mps_dprint(sc, MPS_USER, "%s: This buffer type is not " "supported by the IOC", __func__); return (MPS_DIAG_FAILURE); } /* * Clear some flags initially. */ pBuffer->force_release = FALSE; pBuffer->valid_data = FALSE; pBuffer->owned_by_firmware = FALSE; /* * Get a command. */ cm = mps_alloc_command(sc); if (cm == NULL) { mps_printf(sc, "%s: no mps requests\n", __func__); return (MPS_DIAG_FAILURE); } /* * Build the request for releasing the FW Diag Buffer and send it. */ req = (MPI2_DIAG_RELEASE_REQUEST *)cm->cm_req; req->Function = MPI2_FUNCTION_DIAG_RELEASE; req->BufferType = pBuffer->buffer_type; cm->cm_data = NULL; cm->cm_length = 0; cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; cm->cm_complete_data = NULL; /* * Send command synchronously. */ status = mps_wait_command(sc, &cm, 30, CAN_SLEEP); if (status || (cm == NULL)) { mps_printf(sc, "%s: invalid request: error %d\n", __func__, status); status = MPS_DIAG_FAILURE; goto done; } /* * Process RELEASE reply. */ reply = (MPI2_DIAG_RELEASE_REPLY *)cm->cm_reply; if (reply == NULL) { mps_printf(sc, "%s: reply is NULL, probably due to " "reinitialization\n", __func__); status = MPS_DIAG_FAILURE; goto done; } if (((le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS) || pBuffer->owned_by_firmware) { status = MPS_DIAG_FAILURE; mps_dprint(sc, MPS_FAULT, "%s: release of FW Diag Buffer " "failed with IOCStatus = 0x%x and IOCLogInfo = 0x%x\n", __func__, le16toh(reply->IOCStatus), le32toh(reply->IOCLogInfo)); goto done; } /* * Release was successful. */ *return_code = MPS_FW_DIAG_ERROR_SUCCESS; status = MPS_DIAG_SUCCESS; /* * If this was for an UNREGISTER diag type command, clear the unique ID. */ if (diag_type == MPS_FW_DIAG_TYPE_UNREGISTER) { pBuffer->unique_id = MPS_FW_DIAG_INVALID_UID; } done: if (cm != NULL) mps_free_command(sc, cm); return (status); } static int mps_diag_register(struct mps_softc *sc, mps_fw_diag_register_t *diag_register, uint32_t *return_code) { mps_fw_diagnostic_buffer_t *pBuffer; struct mps_busdma_context *ctx; uint8_t extended_type, buffer_type, i; uint32_t buffer_size; uint32_t unique_id; int status; int error; extended_type = diag_register->ExtendedType; buffer_type = diag_register->BufferType; buffer_size = diag_register->RequestedBufferSize; unique_id = diag_register->UniqueId; ctx = NULL; error = 0; /* * Check for valid buffer type */ if (buffer_type >= MPI2_DIAG_BUF_TYPE_COUNT) { *return_code = MPS_FW_DIAG_ERROR_INVALID_PARAMETER; return (MPS_DIAG_FAILURE); } /* * Get the current buffer and look up the unique ID. The unique ID * should not be found. If it is, the ID is already in use. */ i = mps_get_fw_diag_buffer_number(sc, unique_id); pBuffer = &sc->fw_diag_buffer_list[buffer_type]; if (i != MPS_FW_DIAGNOSTIC_UID_NOT_FOUND) { *return_code = MPS_FW_DIAG_ERROR_INVALID_UID; return (MPS_DIAG_FAILURE); } /* * The buffer's unique ID should not be registered yet, and the given * unique ID cannot be 0. */ if ((pBuffer->unique_id != MPS_FW_DIAG_INVALID_UID) || (unique_id == MPS_FW_DIAG_INVALID_UID)) { *return_code = MPS_FW_DIAG_ERROR_INVALID_UID; return (MPS_DIAG_FAILURE); } /* * If this buffer is already posted as immediate, just change owner. */ if (pBuffer->immediate && pBuffer->owned_by_firmware && (pBuffer->unique_id == MPS_FW_DIAG_INVALID_UID)) { pBuffer->immediate = FALSE; pBuffer->unique_id = unique_id; return (MPS_DIAG_SUCCESS); } /* * Post a new buffer after checking if it's enabled. The DMA buffer * that is allocated will be contiguous (nsegments = 1). */ if (!pBuffer->enabled) { *return_code = MPS_FW_DIAG_ERROR_NO_BUFFER; return (MPS_DIAG_FAILURE); } if (bus_dma_tag_create( sc->mps_parent_dmat, /* parent */ 1, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ buffer_size, /* maxsize */ 1, /* nsegments */ buffer_size, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->fw_diag_dmat)) { mps_dprint(sc, MPS_ERROR, "Cannot allocate FW diag buffer DMA tag\n"); *return_code = MPS_FW_DIAG_ERROR_NO_BUFFER; status = MPS_DIAG_FAILURE; goto bailout; } if (bus_dmamem_alloc(sc->fw_diag_dmat, (void **)&sc->fw_diag_buffer, BUS_DMA_NOWAIT, &sc->fw_diag_map)) { mps_dprint(sc, MPS_ERROR, "Cannot allocate FW diag buffer memory\n"); *return_code = MPS_FW_DIAG_ERROR_NO_BUFFER; status = MPS_DIAG_FAILURE; goto bailout; } bzero(sc->fw_diag_buffer, buffer_size); ctx = malloc(sizeof(*ctx), M_MPSUSER, M_WAITOK | M_ZERO); if (ctx == NULL) { device_printf(sc->mps_dev, "%s: context malloc failed\n", __func__); *return_code = MPS_FW_DIAG_ERROR_NO_BUFFER; status = MPS_DIAG_FAILURE; goto bailout; } ctx->addr = &sc->fw_diag_busaddr; ctx->buffer_dmat = sc->fw_diag_dmat; ctx->buffer_dmamap = sc->fw_diag_map; ctx->softc = sc; error = bus_dmamap_load(sc->fw_diag_dmat, sc->fw_diag_map, sc->fw_diag_buffer, buffer_size, mps_memaddr_wait_cb, ctx, 0); if (error == EINPROGRESS) { /* XXX KDM */ device_printf(sc->mps_dev, "%s: Deferred bus_dmamap_load\n", __func__); /* * Wait for the load to complete. If we're interrupted, * bail out. */ mps_lock(sc); if (ctx->completed == 0) { error = msleep(ctx, &sc->mps_mtx, PCATCH, "mpswait", 0); if (error != 0) { /* * We got an error from msleep(9). This is * most likely due to a signal. Tell * mpr_memaddr_wait_cb() that we've abandoned * the context, so it needs to clean up when * it is called. */ ctx->abandoned = 1; /* The callback will free this memory */ ctx = NULL; mps_unlock(sc); device_printf(sc->mps_dev, "Cannot " "bus_dmamap_load FW diag buffer, error = " "%d returned from msleep\n", error); *return_code = MPS_FW_DIAG_ERROR_NO_BUFFER; status = MPS_DIAG_FAILURE; goto bailout; } } mps_unlock(sc); } if ((error != 0) || (ctx->error != 0)) { device_printf(sc->mps_dev, "Cannot bus_dmamap_load FW diag " "buffer, %serror = %d\n", error ? "" : "callback ", error ? error : ctx->error); *return_code = MPS_FW_DIAG_ERROR_NO_BUFFER; status = MPS_DIAG_FAILURE; goto bailout; } bus_dmamap_sync(sc->fw_diag_dmat, sc->fw_diag_map, BUS_DMASYNC_PREREAD); pBuffer->size = buffer_size; /* * Copy the given info to the diag buffer and post the buffer. */ pBuffer->buffer_type = buffer_type; pBuffer->immediate = FALSE; if (buffer_type == MPI2_DIAG_BUF_TYPE_TRACE) { for (i = 0; i < (sizeof (pBuffer->product_specific) / 4); i++) { pBuffer->product_specific[i] = diag_register->ProductSpecific[i]; } } pBuffer->extended_type = extended_type; pBuffer->unique_id = unique_id; status = mps_post_fw_diag_buffer(sc, pBuffer, return_code); bailout: /* * In case there was a failure, free the DMA buffer. */ if (status == MPS_DIAG_FAILURE) { if (sc->fw_diag_busaddr != 0) { bus_dmamap_unload(sc->fw_diag_dmat, sc->fw_diag_map); sc->fw_diag_busaddr = 0; } if (sc->fw_diag_buffer != NULL) { bus_dmamem_free(sc->fw_diag_dmat, sc->fw_diag_buffer, sc->fw_diag_map); sc->fw_diag_buffer = NULL; } if (sc->fw_diag_dmat != NULL) { bus_dma_tag_destroy(sc->fw_diag_dmat); sc->fw_diag_dmat = NULL; } } if (ctx != NULL) free(ctx, M_MPSUSER); return (status); } static int mps_diag_unregister(struct mps_softc *sc, mps_fw_diag_unregister_t *diag_unregister, uint32_t *return_code) { mps_fw_diagnostic_buffer_t *pBuffer; uint8_t i; uint32_t unique_id; int status; unique_id = diag_unregister->UniqueId; /* * Get the current buffer and look up the unique ID. The unique ID * should be there. */ i = mps_get_fw_diag_buffer_number(sc, unique_id); if (i == MPS_FW_DIAGNOSTIC_UID_NOT_FOUND) { *return_code = MPS_FW_DIAG_ERROR_INVALID_UID; return (MPS_DIAG_FAILURE); } pBuffer = &sc->fw_diag_buffer_list[i]; /* * Try to release the buffer from FW before freeing it. If release * fails, don't free the DMA buffer in case FW tries to access it * later. If buffer is not owned by firmware, can't release it. */ if (!pBuffer->owned_by_firmware) { status = MPS_DIAG_SUCCESS; } else { status = mps_release_fw_diag_buffer(sc, pBuffer, return_code, MPS_FW_DIAG_TYPE_UNREGISTER); } /* * At this point, return the current status no matter what happens with * the DMA buffer. */ pBuffer->unique_id = MPS_FW_DIAG_INVALID_UID; if (status == MPS_DIAG_SUCCESS) { if (sc->fw_diag_busaddr != 0) { bus_dmamap_unload(sc->fw_diag_dmat, sc->fw_diag_map); sc->fw_diag_busaddr = 0; } if (sc->fw_diag_buffer != NULL) { bus_dmamem_free(sc->fw_diag_dmat, sc->fw_diag_buffer, sc->fw_diag_map); sc->fw_diag_buffer = NULL; } if (sc->fw_diag_dmat != NULL) { bus_dma_tag_destroy(sc->fw_diag_dmat); sc->fw_diag_dmat = NULL; } } return (status); } static int mps_diag_query(struct mps_softc *sc, mps_fw_diag_query_t *diag_query, uint32_t *return_code) { mps_fw_diagnostic_buffer_t *pBuffer; uint8_t i; uint32_t unique_id; unique_id = diag_query->UniqueId; /* * If ID is valid, query on ID. * If ID is invalid, query on buffer type. */ if (unique_id == MPS_FW_DIAG_INVALID_UID) { i = diag_query->BufferType; if (i >= MPI2_DIAG_BUF_TYPE_COUNT) { *return_code = MPS_FW_DIAG_ERROR_INVALID_UID; return (MPS_DIAG_FAILURE); } } else { i = mps_get_fw_diag_buffer_number(sc, unique_id); if (i == MPS_FW_DIAGNOSTIC_UID_NOT_FOUND) { *return_code = MPS_FW_DIAG_ERROR_INVALID_UID; return (MPS_DIAG_FAILURE); } } /* * Fill query structure with the diag buffer info. */ pBuffer = &sc->fw_diag_buffer_list[i]; diag_query->BufferType = pBuffer->buffer_type; diag_query->ExtendedType = pBuffer->extended_type; if (diag_query->BufferType == MPI2_DIAG_BUF_TYPE_TRACE) { for (i = 0; i < (sizeof(diag_query->ProductSpecific) / 4); i++) { diag_query->ProductSpecific[i] = pBuffer->product_specific[i]; } } diag_query->TotalBufferSize = pBuffer->size; diag_query->DriverAddedBufferSize = 0; diag_query->UniqueId = pBuffer->unique_id; diag_query->ApplicationFlags = 0; diag_query->DiagnosticFlags = 0; /* * Set/Clear application flags */ if (pBuffer->immediate) { diag_query->ApplicationFlags &= ~MPS_FW_DIAG_FLAG_APP_OWNED; } else { diag_query->ApplicationFlags |= MPS_FW_DIAG_FLAG_APP_OWNED; } if (pBuffer->valid_data || pBuffer->owned_by_firmware) { diag_query->ApplicationFlags |= MPS_FW_DIAG_FLAG_BUFFER_VALID; } else { diag_query->ApplicationFlags &= ~MPS_FW_DIAG_FLAG_BUFFER_VALID; } if (pBuffer->owned_by_firmware) { diag_query->ApplicationFlags |= MPS_FW_DIAG_FLAG_FW_BUFFER_ACCESS; } else { diag_query->ApplicationFlags &= ~MPS_FW_DIAG_FLAG_FW_BUFFER_ACCESS; } return (MPS_DIAG_SUCCESS); } static int mps_diag_read_buffer(struct mps_softc *sc, mps_diag_read_buffer_t *diag_read_buffer, uint8_t *ioctl_buf, uint32_t *return_code) { mps_fw_diagnostic_buffer_t *pBuffer; uint8_t i, *pData; uint32_t unique_id; int status; unique_id = diag_read_buffer->UniqueId; /* * Get the current buffer and look up the unique ID. The unique ID * should be there. */ i = mps_get_fw_diag_buffer_number(sc, unique_id); if (i == MPS_FW_DIAGNOSTIC_UID_NOT_FOUND) { *return_code = MPS_FW_DIAG_ERROR_INVALID_UID; return (MPS_DIAG_FAILURE); } pBuffer = &sc->fw_diag_buffer_list[i]; /* * Make sure requested read is within limits */ if (diag_read_buffer->StartingOffset + diag_read_buffer->BytesToRead > pBuffer->size) { *return_code = MPS_FW_DIAG_ERROR_INVALID_PARAMETER; return (MPS_DIAG_FAILURE); } /* Sync the DMA map before we copy to userland. */ bus_dmamap_sync(sc->fw_diag_dmat, sc->fw_diag_map, BUS_DMASYNC_POSTREAD); /* * Copy the requested data from DMA to the diag_read_buffer. The DMA * buffer that was allocated is one contiguous buffer. */ pData = (uint8_t *)(sc->fw_diag_buffer + diag_read_buffer->StartingOffset); if (copyout(pData, ioctl_buf, diag_read_buffer->BytesToRead) != 0) return (MPS_DIAG_FAILURE); diag_read_buffer->Status = 0; /* * Set or clear the Force Release flag. */ if (pBuffer->force_release) { diag_read_buffer->Flags |= MPS_FW_DIAG_FLAG_FORCE_RELEASE; } else { diag_read_buffer->Flags &= ~MPS_FW_DIAG_FLAG_FORCE_RELEASE; } /* * If buffer is to be reregistered, make sure it's not already owned by * firmware first. */ status = MPS_DIAG_SUCCESS; if (!pBuffer->owned_by_firmware) { if (diag_read_buffer->Flags & MPS_FW_DIAG_FLAG_REREGISTER) { status = mps_post_fw_diag_buffer(sc, pBuffer, return_code); } } return (status); } static int mps_diag_release(struct mps_softc *sc, mps_fw_diag_release_t *diag_release, uint32_t *return_code) { mps_fw_diagnostic_buffer_t *pBuffer; uint8_t i; uint32_t unique_id; int status; unique_id = diag_release->UniqueId; /* * Get the current buffer and look up the unique ID. The unique ID * should be there. */ i = mps_get_fw_diag_buffer_number(sc, unique_id); if (i == MPS_FW_DIAGNOSTIC_UID_NOT_FOUND) { *return_code = MPS_FW_DIAG_ERROR_INVALID_UID; return (MPS_DIAG_FAILURE); } pBuffer = &sc->fw_diag_buffer_list[i]; /* * If buffer is not owned by firmware, it's already been released. */ if (!pBuffer->owned_by_firmware) { *return_code = MPS_FW_DIAG_ERROR_ALREADY_RELEASED; return (MPS_DIAG_FAILURE); } /* * Release the buffer. */ status = mps_release_fw_diag_buffer(sc, pBuffer, return_code, MPS_FW_DIAG_TYPE_RELEASE); return (status); } static int mps_do_diag_action(struct mps_softc *sc, uint32_t action, uint8_t *diag_action, uint32_t length, uint32_t *return_code) { mps_fw_diag_register_t diag_register; mps_fw_diag_unregister_t diag_unregister; mps_fw_diag_query_t diag_query; mps_diag_read_buffer_t diag_read_buffer; mps_fw_diag_release_t diag_release; int status = MPS_DIAG_SUCCESS; uint32_t original_return_code; original_return_code = *return_code; *return_code = MPS_FW_DIAG_ERROR_SUCCESS; switch (action) { case MPS_FW_DIAG_TYPE_REGISTER: if (!length) { *return_code = MPS_FW_DIAG_ERROR_INVALID_PARAMETER; status = MPS_DIAG_FAILURE; break; } if (copyin(diag_action, &diag_register, sizeof(diag_register)) != 0) return (MPS_DIAG_FAILURE); status = mps_diag_register(sc, &diag_register, return_code); break; case MPS_FW_DIAG_TYPE_UNREGISTER: if (length < sizeof(diag_unregister)) { *return_code = MPS_FW_DIAG_ERROR_INVALID_PARAMETER; status = MPS_DIAG_FAILURE; break; } if (copyin(diag_action, &diag_unregister, sizeof(diag_unregister)) != 0) return (MPS_DIAG_FAILURE); status = mps_diag_unregister(sc, &diag_unregister, return_code); break; case MPS_FW_DIAG_TYPE_QUERY: if (length < sizeof (diag_query)) { *return_code = MPS_FW_DIAG_ERROR_INVALID_PARAMETER; status = MPS_DIAG_FAILURE; break; } if (copyin(diag_action, &diag_query, sizeof(diag_query)) != 0) return (MPS_DIAG_FAILURE); status = mps_diag_query(sc, &diag_query, return_code); if (status == MPS_DIAG_SUCCESS) if (copyout(&diag_query, diag_action, sizeof (diag_query)) != 0) return (MPS_DIAG_FAILURE); break; case MPS_FW_DIAG_TYPE_READ_BUFFER: if (copyin(diag_action, &diag_read_buffer, sizeof(diag_read_buffer)) != 0) return (MPS_DIAG_FAILURE); if (length < diag_read_buffer.BytesToRead) { *return_code = MPS_FW_DIAG_ERROR_INVALID_PARAMETER; status = MPS_DIAG_FAILURE; break; } status = mps_diag_read_buffer(sc, &diag_read_buffer, PTRIN(diag_read_buffer.PtrDataBuffer), return_code); if (status == MPS_DIAG_SUCCESS) { if (copyout(&diag_read_buffer, diag_action, sizeof(diag_read_buffer) - sizeof(diag_read_buffer.PtrDataBuffer)) != 0) return (MPS_DIAG_FAILURE); } break; case MPS_FW_DIAG_TYPE_RELEASE: if (length < sizeof(diag_release)) { *return_code = MPS_FW_DIAG_ERROR_INVALID_PARAMETER; status = MPS_DIAG_FAILURE; break; } if (copyin(diag_action, &diag_release, sizeof(diag_release)) != 0) return (MPS_DIAG_FAILURE); status = mps_diag_release(sc, &diag_release, return_code); break; default: *return_code = MPS_FW_DIAG_ERROR_INVALID_PARAMETER; status = MPS_DIAG_FAILURE; break; } if ((status == MPS_DIAG_FAILURE) && (original_return_code == MPS_FW_DIAG_NEW) && (*return_code != MPS_FW_DIAG_ERROR_SUCCESS)) status = MPS_DIAG_SUCCESS; return (status); } static int mps_user_diag_action(struct mps_softc *sc, mps_diag_action_t *data) { int status; /* * Only allow one diag action at one time. */ if (sc->mps_flags & MPS_FLAGS_BUSY) { mps_dprint(sc, MPS_USER, "%s: Only one FW diag command " "allowed at a single time.", __func__); return (EBUSY); } sc->mps_flags |= MPS_FLAGS_BUSY; /* * Send diag action request */ if (data->Action == MPS_FW_DIAG_TYPE_REGISTER || data->Action == MPS_FW_DIAG_TYPE_UNREGISTER || data->Action == MPS_FW_DIAG_TYPE_QUERY || data->Action == MPS_FW_DIAG_TYPE_READ_BUFFER || data->Action == MPS_FW_DIAG_TYPE_RELEASE) { status = mps_do_diag_action(sc, data->Action, PTRIN(data->PtrDiagAction), data->Length, &data->ReturnCode); } else status = EINVAL; sc->mps_flags &= ~MPS_FLAGS_BUSY; return (status); } /* * Copy the event recording mask and the event queue size out. For * clarification, the event recording mask (events_to_record) is not the same * thing as the event mask (event_mask). events_to_record has a bit set for * every event type that is to be recorded by the driver, and event_mask has a * bit cleared for every event that is allowed into the driver from the IOC. * They really have nothing to do with each other. */ static void mps_user_event_query(struct mps_softc *sc, mps_event_query_t *data) { uint8_t i; mps_lock(sc); data->Entries = MPS_EVENT_QUEUE_SIZE; for (i = 0; i < 4; i++) { data->Types[i] = sc->events_to_record[i]; } mps_unlock(sc); } /* * Set the driver's event mask according to what's been given. See * mps_user_event_query for explanation of the event recording mask and the IOC * event mask. It's the app's responsibility to enable event logging by setting * the bits in events_to_record. Initially, no events will be logged. */ static void mps_user_event_enable(struct mps_softc *sc, mps_event_enable_t *data) { uint8_t i; mps_lock(sc); for (i = 0; i < 4; i++) { sc->events_to_record[i] = data->Types[i]; } mps_unlock(sc); } /* * Copy out the events that have been recorded, up to the max events allowed. */ static int mps_user_event_report(struct mps_softc *sc, mps_event_report_t *data) { int status = 0; uint32_t size; mps_lock(sc); size = data->Size; if ((size >= sizeof(sc->recorded_events)) && (status == 0)) { mps_unlock(sc); if (copyout((void *)sc->recorded_events, PTRIN(data->PtrEvents), size) != 0) status = EFAULT; mps_lock(sc); } else { /* * data->Size value is not large enough to copy event data. */ status = EFAULT; } /* * Change size value to match the number of bytes that were copied. */ if (status == 0) data->Size = sizeof(sc->recorded_events); mps_unlock(sc); return (status); } /* * Record events into the driver from the IOC if they are not masked. */ void mpssas_record_event(struct mps_softc *sc, MPI2_EVENT_NOTIFICATION_REPLY *event_reply) { uint32_t event; int i, j; uint16_t event_data_len; boolean_t sendAEN = FALSE; event = event_reply->Event; /* * Generate a system event to let anyone who cares know that a * LOG_ENTRY_ADDED event has occurred. This is sent no matter what the * event mask is set to. */ if (event == MPI2_EVENT_LOG_ENTRY_ADDED) { sendAEN = TRUE; } /* * Record the event only if its corresponding bit is set in * events_to_record. event_index is the index into recorded_events and * event_number is the overall number of an event being recorded since * start-of-day. event_index will roll over; event_number will never * roll over. */ i = (uint8_t)(event / 32); j = (uint8_t)(event % 32); if ((i < 4) && ((1 << j) & sc->events_to_record[i])) { i = sc->event_index; sc->recorded_events[i].Type = event; sc->recorded_events[i].Number = ++sc->event_number; bzero(sc->recorded_events[i].Data, MPS_MAX_EVENT_DATA_LENGTH * 4); event_data_len = event_reply->EventDataLength; if (event_data_len > 0) { /* * Limit data to size in m_event entry */ if (event_data_len > MPS_MAX_EVENT_DATA_LENGTH) { event_data_len = MPS_MAX_EVENT_DATA_LENGTH; } for (j = 0; j < event_data_len; j++) { sc->recorded_events[i].Data[j] = event_reply->EventData[j]; } /* * check for index wrap-around */ if (++i == MPS_EVENT_QUEUE_SIZE) { i = 0; } sc->event_index = (uint8_t)i; /* * Set flag to send the event. */ sendAEN = TRUE; } } /* * Generate a system event if flag is set to let anyone who cares know * that an event has occurred. */ if (sendAEN) { //SLM-how to send a system event (see kqueue, kevent) // (void) ddi_log_sysevent(mpt->m_dip, DDI_VENDOR_LSI, "MPT_SAS", // "SAS", NULL, NULL, DDI_NOSLEEP); } } static int mps_user_reg_access(struct mps_softc *sc, mps_reg_access_t *data) { int status = 0; switch (data->Command) { /* * IO access is not supported. */ case REG_IO_READ: case REG_IO_WRITE: mps_dprint(sc, MPS_USER, "IO access is not supported. " "Use memory access."); status = EINVAL; break; case REG_MEM_READ: data->RegData = mps_regread(sc, data->RegOffset); break; case REG_MEM_WRITE: mps_regwrite(sc, data->RegOffset, data->RegData); break; default: status = EINVAL; break; } return (status); } static int mps_user_btdh(struct mps_softc *sc, mps_btdh_mapping_t *data) { uint8_t bt2dh = FALSE; uint8_t dh2bt = FALSE; uint16_t dev_handle, bus, target; bus = data->Bus; target = data->TargetID; dev_handle = data->DevHandle; /* * When DevHandle is 0xFFFF and Bus/Target are not 0xFFFF, use Bus/ * Target to get DevHandle. When Bus/Target are 0xFFFF and DevHandle is * not 0xFFFF, use DevHandle to get Bus/Target. Anything else is * invalid. */ if ((bus == 0xFFFF) && (target == 0xFFFF) && (dev_handle != 0xFFFF)) dh2bt = TRUE; if ((dev_handle == 0xFFFF) && (bus != 0xFFFF) && (target != 0xFFFF)) bt2dh = TRUE; if (!dh2bt && !bt2dh) return (EINVAL); /* * Only handle bus of 0. Make sure target is within range. */ if (bt2dh) { if (bus != 0) return (EINVAL); if (target > sc->max_devices) { mps_dprint(sc, MPS_FAULT, "Target ID is out of range " "for Bus/Target to DevHandle mapping."); return (EINVAL); } dev_handle = sc->mapping_table[target].dev_handle; if (dev_handle) data->DevHandle = dev_handle; } else { bus = 0; target = mps_mapping_get_tid_from_handle(sc, dev_handle); data->Bus = bus; data->TargetID = target; } return (0); } static int mps_ioctl(struct cdev *dev, u_long cmd, void *arg, int flag, struct thread *td) { struct mps_softc *sc; struct mps_cfg_page_req *page_req; struct mps_ext_cfg_page_req *ext_page_req; void *mps_page; int error, msleep_ret; mps_page = NULL; sc = dev->si_drv1; page_req = (void *)arg; ext_page_req = (void *)arg; switch (cmd) { case MPSIO_READ_CFG_HEADER: mps_lock(sc); error = mps_user_read_cfg_header(sc, page_req); mps_unlock(sc); break; case MPSIO_READ_CFG_PAGE: mps_page = malloc(page_req->len, M_MPSUSER, M_WAITOK | M_ZERO); error = copyin(page_req->buf, mps_page, sizeof(MPI2_CONFIG_PAGE_HEADER)); if (error) break; mps_lock(sc); error = mps_user_read_cfg_page(sc, page_req, mps_page); mps_unlock(sc); if (error) break; error = copyout(mps_page, page_req->buf, page_req->len); break; case MPSIO_READ_EXT_CFG_HEADER: mps_lock(sc); error = mps_user_read_extcfg_header(sc, ext_page_req); mps_unlock(sc); break; case MPSIO_READ_EXT_CFG_PAGE: mps_page = malloc(ext_page_req->len, M_MPSUSER, M_WAITOK|M_ZERO); error = copyin(ext_page_req->buf, mps_page, sizeof(MPI2_CONFIG_EXTENDED_PAGE_HEADER)); if (error) break; mps_lock(sc); error = mps_user_read_extcfg_page(sc, ext_page_req, mps_page); mps_unlock(sc); if (error) break; error = copyout(mps_page, ext_page_req->buf, ext_page_req->len); break; case MPSIO_WRITE_CFG_PAGE: mps_page = malloc(page_req->len, M_MPSUSER, M_WAITOK|M_ZERO); error = copyin(page_req->buf, mps_page, page_req->len); if (error) break; mps_lock(sc); error = mps_user_write_cfg_page(sc, page_req, mps_page); mps_unlock(sc); break; case MPSIO_MPS_COMMAND: error = mps_user_command(sc, (struct mps_usr_command *)arg); break; case MPTIOCTL_PASS_THRU: /* * The user has requested to pass through a command to be * executed by the MPT firmware. Call our routine which does * this. Only allow one passthru IOCTL at one time. */ error = mps_user_pass_thru(sc, (mps_pass_thru_t *)arg); break; case MPTIOCTL_GET_ADAPTER_DATA: /* * The user has requested to read adapter data. Call our * routine which does this. */ error = 0; mps_user_get_adapter_data(sc, (mps_adapter_data_t *)arg); break; case MPTIOCTL_GET_PCI_INFO: /* * The user has requested to read pci info. Call * our routine which does this. */ mps_lock(sc); error = 0; mps_user_read_pci_info(sc, (mps_pci_info_t *)arg); mps_unlock(sc); break; case MPTIOCTL_RESET_ADAPTER: mps_lock(sc); sc->port_enable_complete = 0; uint32_t reinit_start = time_uptime; error = mps_reinit(sc); /* Sleep for 300 second. */ msleep_ret = msleep(&sc->port_enable_complete, &sc->mps_mtx, PRIBIO, "mps_porten", 300 * hz); mps_unlock(sc); if (msleep_ret) printf("Port Enable did not complete after Diag " "Reset msleep error %d.\n", msleep_ret); else mps_dprint(sc, MPS_USER, "Hard Reset with Port Enable completed in %d seconds.\n", (uint32_t) (time_uptime - reinit_start)); break; case MPTIOCTL_DIAG_ACTION: /* * The user has done a diag buffer action. Call our routine * which does this. Only allow one diag action at one time. */ mps_lock(sc); error = mps_user_diag_action(sc, (mps_diag_action_t *)arg); mps_unlock(sc); break; case MPTIOCTL_EVENT_QUERY: /* * The user has done an event query. Call our routine which does * this. */ error = 0; mps_user_event_query(sc, (mps_event_query_t *)arg); break; case MPTIOCTL_EVENT_ENABLE: /* * The user has done an event enable. Call our routine which * does this. */ error = 0; mps_user_event_enable(sc, (mps_event_enable_t *)arg); break; case MPTIOCTL_EVENT_REPORT: /* * The user has done an event report. Call our routine which * does this. */ error = mps_user_event_report(sc, (mps_event_report_t *)arg); break; case MPTIOCTL_REG_ACCESS: /* * The user has requested register access. Call our routine * which does this. */ mps_lock(sc); error = mps_user_reg_access(sc, (mps_reg_access_t *)arg); mps_unlock(sc); break; case MPTIOCTL_BTDH_MAPPING: /* * The user has requested to translate a bus/target to a * DevHandle or a DevHandle to a bus/target. Call our routine * which does this. */ error = mps_user_btdh(sc, (mps_btdh_mapping_t *)arg); break; default: error = ENOIOCTL; break; } if (mps_page != NULL) free(mps_page, M_MPSUSER); return (error); } #ifdef COMPAT_FREEBSD32 struct mps_cfg_page_req32 { MPI2_CONFIG_PAGE_HEADER header; uint32_t page_address; uint32_t buf; int len; uint16_t ioc_status; }; struct mps_ext_cfg_page_req32 { MPI2_CONFIG_EXTENDED_PAGE_HEADER header; uint32_t page_address; uint32_t buf; int len; uint16_t ioc_status; }; struct mps_raid_action32 { uint8_t action; uint8_t volume_bus; uint8_t volume_id; uint8_t phys_disk_num; uint32_t action_data_word; uint32_t buf; int len; uint32_t volume_status; uint32_t action_data[4]; uint16_t action_status; uint16_t ioc_status; uint8_t write; }; struct mps_usr_command32 { uint32_t req; uint32_t req_len; uint32_t rpl; uint32_t rpl_len; uint32_t buf; int len; uint32_t flags; }; #define MPSIO_READ_CFG_HEADER32 _IOWR('M', 200, struct mps_cfg_page_req32) #define MPSIO_READ_CFG_PAGE32 _IOWR('M', 201, struct mps_cfg_page_req32) #define MPSIO_READ_EXT_CFG_HEADER32 _IOWR('M', 202, struct mps_ext_cfg_page_req32) #define MPSIO_READ_EXT_CFG_PAGE32 _IOWR('M', 203, struct mps_ext_cfg_page_req32) #define MPSIO_WRITE_CFG_PAGE32 _IOWR('M', 204, struct mps_cfg_page_req32) #define MPSIO_RAID_ACTION32 _IOWR('M', 205, struct mps_raid_action32) #define MPSIO_MPS_COMMAND32 _IOWR('M', 210, struct mps_usr_command32) static int mps_ioctl32(struct cdev *dev, u_long cmd32, void *_arg, int flag, struct thread *td) { struct mps_cfg_page_req32 *page32 = _arg; struct mps_ext_cfg_page_req32 *ext32 = _arg; struct mps_raid_action32 *raid32 = _arg; struct mps_usr_command32 *user32 = _arg; union { struct mps_cfg_page_req page; struct mps_ext_cfg_page_req ext; struct mps_raid_action raid; struct mps_usr_command user; } arg; u_long cmd; int error; switch (cmd32) { case MPSIO_READ_CFG_HEADER32: case MPSIO_READ_CFG_PAGE32: case MPSIO_WRITE_CFG_PAGE32: if (cmd32 == MPSIO_READ_CFG_HEADER32) cmd = MPSIO_READ_CFG_HEADER; else if (cmd32 == MPSIO_READ_CFG_PAGE32) cmd = MPSIO_READ_CFG_PAGE; else cmd = MPSIO_WRITE_CFG_PAGE; CP(*page32, arg.page, header); CP(*page32, arg.page, page_address); PTRIN_CP(*page32, arg.page, buf); CP(*page32, arg.page, len); CP(*page32, arg.page, ioc_status); break; case MPSIO_READ_EXT_CFG_HEADER32: case MPSIO_READ_EXT_CFG_PAGE32: if (cmd32 == MPSIO_READ_EXT_CFG_HEADER32) cmd = MPSIO_READ_EXT_CFG_HEADER; else cmd = MPSIO_READ_EXT_CFG_PAGE; CP(*ext32, arg.ext, header); CP(*ext32, arg.ext, page_address); PTRIN_CP(*ext32, arg.ext, buf); CP(*ext32, arg.ext, len); CP(*ext32, arg.ext, ioc_status); break; case MPSIO_RAID_ACTION32: cmd = MPSIO_RAID_ACTION; CP(*raid32, arg.raid, action); CP(*raid32, arg.raid, volume_bus); CP(*raid32, arg.raid, volume_id); CP(*raid32, arg.raid, phys_disk_num); CP(*raid32, arg.raid, action_data_word); PTRIN_CP(*raid32, arg.raid, buf); CP(*raid32, arg.raid, len); CP(*raid32, arg.raid, volume_status); bcopy(raid32->action_data, arg.raid.action_data, sizeof arg.raid.action_data); CP(*raid32, arg.raid, ioc_status); CP(*raid32, arg.raid, write); break; case MPSIO_MPS_COMMAND32: cmd = MPSIO_MPS_COMMAND; PTRIN_CP(*user32, arg.user, req); CP(*user32, arg.user, req_len); PTRIN_CP(*user32, arg.user, rpl); CP(*user32, arg.user, rpl_len); PTRIN_CP(*user32, arg.user, buf); CP(*user32, arg.user, len); CP(*user32, arg.user, flags); break; default: return (ENOIOCTL); } error = mps_ioctl(dev, cmd, &arg, flag, td); if (error == 0 && (cmd32 & IOC_OUT) != 0) { switch (cmd32) { case MPSIO_READ_CFG_HEADER32: case MPSIO_READ_CFG_PAGE32: case MPSIO_WRITE_CFG_PAGE32: CP(arg.page, *page32, header); CP(arg.page, *page32, page_address); PTROUT_CP(arg.page, *page32, buf); CP(arg.page, *page32, len); CP(arg.page, *page32, ioc_status); break; case MPSIO_READ_EXT_CFG_HEADER32: case MPSIO_READ_EXT_CFG_PAGE32: CP(arg.ext, *ext32, header); CP(arg.ext, *ext32, page_address); PTROUT_CP(arg.ext, *ext32, buf); CP(arg.ext, *ext32, len); CP(arg.ext, *ext32, ioc_status); break; case MPSIO_RAID_ACTION32: CP(arg.raid, *raid32, action); CP(arg.raid, *raid32, volume_bus); CP(arg.raid, *raid32, volume_id); CP(arg.raid, *raid32, phys_disk_num); CP(arg.raid, *raid32, action_data_word); PTROUT_CP(arg.raid, *raid32, buf); CP(arg.raid, *raid32, len); CP(arg.raid, *raid32, volume_status); bcopy(arg.raid.action_data, raid32->action_data, sizeof arg.raid.action_data); CP(arg.raid, *raid32, ioc_status); CP(arg.raid, *raid32, write); break; case MPSIO_MPS_COMMAND32: PTROUT_CP(arg.user, *user32, req); CP(arg.user, *user32, req_len); PTROUT_CP(arg.user, *user32, rpl); CP(arg.user, *user32, rpl_len); PTROUT_CP(arg.user, *user32, buf); CP(arg.user, *user32, len); CP(arg.user, *user32, flags); break; } } return (error); } #endif /* COMPAT_FREEBSD32 */ static int mps_ioctl_devsw(struct cdev *dev, u_long com, caddr_t arg, int flag, struct thread *td) { #ifdef COMPAT_FREEBSD32 if (SV_CURPROC_FLAG(SV_ILP32)) return (mps_ioctl32(dev, com, arg, flag, td)); #endif return (mps_ioctl(dev, com, arg, flag, td)); } Index: stable/12/sys/dev/mpt/mpt_user.c =================================================================== --- stable/12/sys/dev/mpt/mpt_user.c (revision 360445) +++ stable/12/sys/dev/mpt/mpt_user.c (revision 360446) @@ -1,803 +1,801 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 2008 Yahoo!, Inc. * All rights reserved. * Written by: John Baldwin * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * LSI MPT-Fusion Host Adapter FreeBSD userland interface */ #include __FBSDID("$FreeBSD$"); #include +#ifdef __amd64__ +#include +#endif #include #include #include #include #include struct mpt_user_raid_action_result { uint32_t volume_status; uint32_t action_data[4]; uint16_t action_status; }; struct mpt_page_memory { bus_dma_tag_t tag; bus_dmamap_t map; bus_addr_t paddr; void *vaddr; }; static mpt_probe_handler_t mpt_user_probe; static mpt_attach_handler_t mpt_user_attach; static mpt_enable_handler_t mpt_user_enable; static mpt_ready_handler_t mpt_user_ready; static mpt_event_handler_t mpt_user_event; static mpt_reset_handler_t mpt_user_reset; static mpt_detach_handler_t mpt_user_detach; static struct mpt_personality mpt_user_personality = { .name = "mpt_user", .probe = mpt_user_probe, .attach = mpt_user_attach, .enable = mpt_user_enable, .ready = mpt_user_ready, .event = mpt_user_event, .reset = mpt_user_reset, .detach = mpt_user_detach, }; DECLARE_MPT_PERSONALITY(mpt_user, SI_ORDER_SECOND); static mpt_reply_handler_t mpt_user_reply_handler; static d_open_t mpt_open; static d_close_t mpt_close; static d_ioctl_t mpt_ioctl; static struct cdevsw mpt_cdevsw = { .d_version = D_VERSION, .d_flags = 0, .d_open = mpt_open, .d_close = mpt_close, .d_ioctl = mpt_ioctl, .d_name = "mpt", }; static MALLOC_DEFINE(M_MPTUSER, "mpt_user", "Buffers for mpt(4) ioctls"); static uint32_t user_handler_id = MPT_HANDLER_ID_NONE; static int mpt_user_probe(struct mpt_softc *mpt) { /* Attach to every controller. */ return (0); } static int mpt_user_attach(struct mpt_softc *mpt) { mpt_handler_t handler; int error, unit; MPT_LOCK(mpt); handler.reply_handler = mpt_user_reply_handler; error = mpt_register_handler(mpt, MPT_HANDLER_REPLY, handler, &user_handler_id); MPT_UNLOCK(mpt); if (error != 0) { mpt_prt(mpt, "Unable to register user handler!\n"); return (error); } unit = device_get_unit(mpt->dev); mpt->cdev = make_dev(&mpt_cdevsw, unit, UID_ROOT, GID_OPERATOR, 0640, "mpt%d", unit); if (mpt->cdev == NULL) { MPT_LOCK(mpt); mpt_deregister_handler(mpt, MPT_HANDLER_REPLY, handler, user_handler_id); MPT_UNLOCK(mpt); return (ENOMEM); } mpt->cdev->si_drv1 = mpt; return (0); } static int mpt_user_enable(struct mpt_softc *mpt) { return (0); } static void mpt_user_ready(struct mpt_softc *mpt) { } static int mpt_user_event(struct mpt_softc *mpt, request_t *req, MSG_EVENT_NOTIFY_REPLY *msg) { /* Someday we may want to let a user daemon listen for events? */ return (0); } static void mpt_user_reset(struct mpt_softc *mpt, int type) { } static void mpt_user_detach(struct mpt_softc *mpt) { mpt_handler_t handler; /* XXX: do a purge of pending requests? */ destroy_dev(mpt->cdev); MPT_LOCK(mpt); handler.reply_handler = mpt_user_reply_handler; mpt_deregister_handler(mpt, MPT_HANDLER_REPLY, handler, user_handler_id); MPT_UNLOCK(mpt); } static int mpt_open(struct cdev *dev, int flags, int fmt, struct thread *td) { return (0); } static int mpt_close(struct cdev *dev, int flags, int fmt, struct thread *td) { return (0); } static int mpt_alloc_buffer(struct mpt_softc *mpt, struct mpt_page_memory *page_mem, size_t len) { struct mpt_map_info mi; int error; page_mem->vaddr = NULL; /* Limit requests to 16M. */ if (len > 16 * 1024 * 1024) return (ENOSPC); error = mpt_dma_tag_create(mpt, mpt->parent_dmat, 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, len, 1, len, 0, &page_mem->tag); if (error) return (error); error = bus_dmamem_alloc(page_mem->tag, &page_mem->vaddr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &page_mem->map); if (error) { bus_dma_tag_destroy(page_mem->tag); return (error); } mi.mpt = mpt; error = bus_dmamap_load(page_mem->tag, page_mem->map, page_mem->vaddr, len, mpt_map_rquest, &mi, BUS_DMA_NOWAIT); if (error == 0) error = mi.error; if (error) { bus_dmamem_free(page_mem->tag, page_mem->vaddr, page_mem->map); bus_dma_tag_destroy(page_mem->tag); page_mem->vaddr = NULL; return (error); } page_mem->paddr = mi.phys; return (0); } static void mpt_free_buffer(struct mpt_page_memory *page_mem) { if (page_mem->vaddr == NULL) return; bus_dmamap_unload(page_mem->tag, page_mem->map); bus_dmamem_free(page_mem->tag, page_mem->vaddr, page_mem->map); bus_dma_tag_destroy(page_mem->tag); page_mem->vaddr = NULL; } static int mpt_user_read_cfg_header(struct mpt_softc *mpt, struct mpt_cfg_page_req *page_req) { request_t *req; cfgparms_t params; MSG_CONFIG *cfgp; int error; req = mpt_get_request(mpt, TRUE); if (req == NULL) { mpt_prt(mpt, "mpt_user_read_cfg_header: Get request failed!\n"); return (ENOMEM); } params.Action = MPI_CONFIG_ACTION_PAGE_HEADER; params.PageVersion = 0; params.PageLength = 0; params.PageNumber = page_req->header.PageNumber; params.PageType = page_req->header.PageType; params.PageAddress = le32toh(page_req->page_address); error = mpt_issue_cfg_req(mpt, req, ¶ms, /*addr*/0, /*len*/0, TRUE, 5000); if (error != 0) { /* * Leave the request. Without resetting the chip, it's * still owned by it and we'll just get into trouble * freeing it now. Mark it as abandoned so that if it * shows up later it can be freed. */ mpt_prt(mpt, "read_cfg_header timed out\n"); return (ETIMEDOUT); } page_req->ioc_status = htole16(req->IOCStatus); if ((req->IOCStatus & MPI_IOCSTATUS_MASK) == MPI_IOCSTATUS_SUCCESS) { cfgp = req->req_vbuf; bcopy(&cfgp->Header, &page_req->header, sizeof(page_req->header)); } mpt_free_request(mpt, req); return (0); } static int mpt_user_read_cfg_page(struct mpt_softc *mpt, struct mpt_cfg_page_req *page_req, struct mpt_page_memory *mpt_page) { CONFIG_PAGE_HEADER *hdr; request_t *req; cfgparms_t params; int error; req = mpt_get_request(mpt, TRUE); if (req == NULL) { mpt_prt(mpt, "mpt_user_read_cfg_page: Get request failed!\n"); return (ENOMEM); } hdr = mpt_page->vaddr; params.Action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; params.PageVersion = hdr->PageVersion; params.PageLength = hdr->PageLength; params.PageNumber = hdr->PageNumber; params.PageType = hdr->PageType & MPI_CONFIG_PAGETYPE_MASK; params.PageAddress = le32toh(page_req->page_address); bus_dmamap_sync(mpt_page->tag, mpt_page->map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); error = mpt_issue_cfg_req(mpt, req, ¶ms, mpt_page->paddr, le32toh(page_req->len), TRUE, 5000); if (error != 0) { mpt_prt(mpt, "mpt_user_read_cfg_page timed out\n"); return (ETIMEDOUT); } page_req->ioc_status = htole16(req->IOCStatus); if ((req->IOCStatus & MPI_IOCSTATUS_MASK) == MPI_IOCSTATUS_SUCCESS) bus_dmamap_sync(mpt_page->tag, mpt_page->map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); mpt_free_request(mpt, req); return (0); } static int mpt_user_read_extcfg_header(struct mpt_softc *mpt, struct mpt_ext_cfg_page_req *ext_page_req) { request_t *req; cfgparms_t params; MSG_CONFIG_REPLY *cfgp; int error; req = mpt_get_request(mpt, TRUE); if (req == NULL) { mpt_prt(mpt, "mpt_user_read_extcfg_header: Get request failed!\n"); return (ENOMEM); } params.Action = MPI_CONFIG_ACTION_PAGE_HEADER; params.PageVersion = ext_page_req->header.PageVersion; params.PageLength = 0; params.PageNumber = ext_page_req->header.PageNumber; params.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; params.PageAddress = le32toh(ext_page_req->page_address); params.ExtPageType = ext_page_req->header.ExtPageType; params.ExtPageLength = 0; error = mpt_issue_cfg_req(mpt, req, ¶ms, /*addr*/0, /*len*/0, TRUE, 5000); if (error != 0) { /* * Leave the request. Without resetting the chip, it's * still owned by it and we'll just get into trouble * freeing it now. Mark it as abandoned so that if it * shows up later it can be freed. */ mpt_prt(mpt, "mpt_user_read_extcfg_header timed out\n"); return (ETIMEDOUT); } ext_page_req->ioc_status = htole16(req->IOCStatus); if ((req->IOCStatus & MPI_IOCSTATUS_MASK) == MPI_IOCSTATUS_SUCCESS) { cfgp = req->req_vbuf; ext_page_req->header.PageVersion = cfgp->Header.PageVersion; ext_page_req->header.PageNumber = cfgp->Header.PageNumber; ext_page_req->header.PageType = cfgp->Header.PageType; ext_page_req->header.ExtPageLength = cfgp->ExtPageLength; ext_page_req->header.ExtPageType = cfgp->ExtPageType; } mpt_free_request(mpt, req); return (0); } static int mpt_user_read_extcfg_page(struct mpt_softc *mpt, struct mpt_ext_cfg_page_req *ext_page_req, struct mpt_page_memory *mpt_page) { CONFIG_EXTENDED_PAGE_HEADER *hdr; request_t *req; cfgparms_t params; int error; req = mpt_get_request(mpt, TRUE); if (req == NULL) { mpt_prt(mpt, "mpt_user_read_extcfg_page: Get request failed!\n"); return (ENOMEM); } hdr = mpt_page->vaddr; params.Action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; params.PageVersion = hdr->PageVersion; params.PageLength = 0; params.PageNumber = hdr->PageNumber; params.PageType = MPI_CONFIG_PAGETYPE_EXTENDED; params.PageAddress = le32toh(ext_page_req->page_address); params.ExtPageType = hdr->ExtPageType; params.ExtPageLength = hdr->ExtPageLength; bus_dmamap_sync(mpt_page->tag, mpt_page->map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); error = mpt_issue_cfg_req(mpt, req, ¶ms, mpt_page->paddr, le32toh(ext_page_req->len), TRUE, 5000); if (error != 0) { mpt_prt(mpt, "mpt_user_read_extcfg_page timed out\n"); return (ETIMEDOUT); } ext_page_req->ioc_status = htole16(req->IOCStatus); if ((req->IOCStatus & MPI_IOCSTATUS_MASK) == MPI_IOCSTATUS_SUCCESS) bus_dmamap_sync(mpt_page->tag, mpt_page->map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); mpt_free_request(mpt, req); return (0); } static int mpt_user_write_cfg_page(struct mpt_softc *mpt, struct mpt_cfg_page_req *page_req, struct mpt_page_memory *mpt_page) { CONFIG_PAGE_HEADER *hdr; request_t *req; cfgparms_t params; u_int hdr_attr; int error; hdr = mpt_page->vaddr; hdr_attr = hdr->PageType & MPI_CONFIG_PAGEATTR_MASK; if (hdr_attr != MPI_CONFIG_PAGEATTR_CHANGEABLE && hdr_attr != MPI_CONFIG_PAGEATTR_PERSISTENT) { mpt_prt(mpt, "page type 0x%x not changeable\n", hdr->PageType & MPI_CONFIG_PAGETYPE_MASK); return (EINVAL); } #if 0 /* * We shouldn't mask off other bits here. */ hdr->PageType &= ~MPI_CONFIG_PAGETYPE_MASK; #endif req = mpt_get_request(mpt, TRUE); if (req == NULL) return (ENOMEM); bus_dmamap_sync(mpt_page->tag, mpt_page->map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); /* * There isn't any point in restoring stripped out attributes * if you then mask them going down to issue the request. */ params.Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; params.PageVersion = hdr->PageVersion; params.PageLength = hdr->PageLength; params.PageNumber = hdr->PageNumber; params.PageAddress = le32toh(page_req->page_address); #if 0 /* Restore stripped out attributes */ hdr->PageType |= hdr_attr; params.PageType = hdr->PageType & MPI_CONFIG_PAGETYPE_MASK; #else params.PageType = hdr->PageType; #endif error = mpt_issue_cfg_req(mpt, req, ¶ms, mpt_page->paddr, le32toh(page_req->len), TRUE, 5000); if (error != 0) { mpt_prt(mpt, "mpt_write_cfg_page timed out\n"); return (ETIMEDOUT); } page_req->ioc_status = htole16(req->IOCStatus); bus_dmamap_sync(mpt_page->tag, mpt_page->map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); mpt_free_request(mpt, req); return (0); } static int mpt_user_reply_handler(struct mpt_softc *mpt, request_t *req, uint32_t reply_desc, MSG_DEFAULT_REPLY *reply_frame) { MSG_RAID_ACTION_REPLY *reply; struct mpt_user_raid_action_result *res; if (req == NULL) return (TRUE); if (reply_frame != NULL) { reply = (MSG_RAID_ACTION_REPLY *)reply_frame; req->IOCStatus = le16toh(reply->IOCStatus); res = (struct mpt_user_raid_action_result *) (((uint8_t *)req->req_vbuf) + MPT_RQSL(mpt)); res->action_status = reply->ActionStatus; res->volume_status = reply->VolumeStatus; bcopy(&reply->ActionData, res->action_data, sizeof(res->action_data)); } req->state &= ~REQ_STATE_QUEUED; req->state |= REQ_STATE_DONE; TAILQ_REMOVE(&mpt->request_pending_list, req, links); if ((req->state & REQ_STATE_NEED_WAKEUP) != 0) { wakeup(req); } else if ((req->state & REQ_STATE_TIMEDOUT) != 0) { /* * Whew- we can free this request (late completion) */ mpt_free_request(mpt, req); } return (TRUE); } /* * We use the first part of the request buffer after the request frame * to hold the action data and action status from the RAID reply. The * rest of the request buffer is used to hold the buffer for the * action SGE. */ static int mpt_user_raid_action(struct mpt_softc *mpt, struct mpt_raid_action *raid_act, struct mpt_page_memory *mpt_page) { request_t *req; struct mpt_user_raid_action_result *res; MSG_RAID_ACTION_REQUEST *rap; SGE_SIMPLE32 *se; int error; req = mpt_get_request(mpt, TRUE); if (req == NULL) return (ENOMEM); rap = req->req_vbuf; memset(rap, 0, sizeof *rap); rap->Action = raid_act->action; rap->ActionDataWord = raid_act->action_data_word; rap->Function = MPI_FUNCTION_RAID_ACTION; rap->VolumeID = raid_act->volume_id; rap->VolumeBus = raid_act->volume_bus; rap->PhysDiskNum = raid_act->phys_disk_num; se = (SGE_SIMPLE32 *)&rap->ActionDataSGE; if (mpt_page->vaddr != NULL && raid_act->len != 0) { bus_dmamap_sync(mpt_page->tag, mpt_page->map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); se->Address = htole32(mpt_page->paddr); MPI_pSGE_SET_LENGTH(se, le32toh(raid_act->len)); MPI_pSGE_SET_FLAGS(se, (MPI_SGE_FLAGS_SIMPLE_ELEMENT | MPI_SGE_FLAGS_LAST_ELEMENT | MPI_SGE_FLAGS_END_OF_BUFFER | MPI_SGE_FLAGS_END_OF_LIST | (raid_act->write ? MPI_SGE_FLAGS_HOST_TO_IOC : MPI_SGE_FLAGS_IOC_TO_HOST))); } se->FlagsLength = htole32(se->FlagsLength); rap->MsgContext = htole32(req->index | user_handler_id); mpt_check_doorbell(mpt); mpt_send_cmd(mpt, req); error = mpt_wait_req(mpt, req, REQ_STATE_DONE, REQ_STATE_DONE, TRUE, 2000); if (error != 0) { /* * Leave request so it can be cleaned up later. */ mpt_prt(mpt, "mpt_user_raid_action timed out\n"); return (error); } raid_act->ioc_status = htole16(req->IOCStatus); if ((req->IOCStatus & MPI_IOCSTATUS_MASK) != MPI_IOCSTATUS_SUCCESS) { mpt_free_request(mpt, req); return (0); } res = (struct mpt_user_raid_action_result *) (((uint8_t *)req->req_vbuf) + MPT_RQSL(mpt)); raid_act->volume_status = res->volume_status; raid_act->action_status = res->action_status; bcopy(res->action_data, raid_act->action_data, sizeof(res->action_data)); if (mpt_page->vaddr != NULL) bus_dmamap_sync(mpt_page->tag, mpt_page->map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); mpt_free_request(mpt, req); return (0); } - -#ifdef __amd64__ -#define PTRIN(p) ((void *)(uintptr_t)(p)) -#define PTROUT(v) ((u_int32_t)(uintptr_t)(v)) -#endif static int mpt_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) { struct mpt_softc *mpt; struct mpt_cfg_page_req *page_req; struct mpt_ext_cfg_page_req *ext_page_req; struct mpt_raid_action *raid_act; struct mpt_page_memory mpt_page; #ifdef __amd64__ struct mpt_cfg_page_req32 *page_req32; struct mpt_cfg_page_req page_req_swab; struct mpt_ext_cfg_page_req32 *ext_page_req32; struct mpt_ext_cfg_page_req ext_page_req_swab; struct mpt_raid_action32 *raid_act32; struct mpt_raid_action raid_act_swab; #endif int error; mpt = dev->si_drv1; page_req = (void *)arg; ext_page_req = (void *)arg; raid_act = (void *)arg; mpt_page.vaddr = NULL; #ifdef __amd64__ /* Convert 32-bit structs to native ones. */ page_req32 = (void *)arg; ext_page_req32 = (void *)arg; raid_act32 = (void *)arg; switch (cmd) { case MPTIO_READ_CFG_HEADER32: case MPTIO_READ_CFG_PAGE32: case MPTIO_WRITE_CFG_PAGE32: page_req = &page_req_swab; page_req->header = page_req32->header; page_req->page_address = page_req32->page_address; page_req->buf = PTRIN(page_req32->buf); page_req->len = page_req32->len; page_req->ioc_status = page_req32->ioc_status; break; case MPTIO_READ_EXT_CFG_HEADER32: case MPTIO_READ_EXT_CFG_PAGE32: ext_page_req = &ext_page_req_swab; ext_page_req->header = ext_page_req32->header; ext_page_req->page_address = ext_page_req32->page_address; ext_page_req->buf = PTRIN(ext_page_req32->buf); ext_page_req->len = ext_page_req32->len; ext_page_req->ioc_status = ext_page_req32->ioc_status; break; case MPTIO_RAID_ACTION32: raid_act = &raid_act_swab; raid_act->action = raid_act32->action; raid_act->volume_bus = raid_act32->volume_bus; raid_act->volume_id = raid_act32->volume_id; raid_act->phys_disk_num = raid_act32->phys_disk_num; raid_act->action_data_word = raid_act32->action_data_word; raid_act->buf = PTRIN(raid_act32->buf); raid_act->len = raid_act32->len; raid_act->volume_status = raid_act32->volume_status; bcopy(raid_act32->action_data, raid_act->action_data, sizeof(raid_act->action_data)); raid_act->action_status = raid_act32->action_status; raid_act->ioc_status = raid_act32->ioc_status; raid_act->write = raid_act32->write; break; } #endif switch (cmd) { #ifdef __amd64__ case MPTIO_READ_CFG_HEADER32: #endif case MPTIO_READ_CFG_HEADER: MPT_LOCK(mpt); error = mpt_user_read_cfg_header(mpt, page_req); MPT_UNLOCK(mpt); break; #ifdef __amd64__ case MPTIO_READ_CFG_PAGE32: #endif case MPTIO_READ_CFG_PAGE: error = mpt_alloc_buffer(mpt, &mpt_page, page_req->len); if (error) break; error = copyin(page_req->buf, mpt_page.vaddr, sizeof(CONFIG_PAGE_HEADER)); if (error) break; MPT_LOCK(mpt); error = mpt_user_read_cfg_page(mpt, page_req, &mpt_page); MPT_UNLOCK(mpt); if (error) break; error = copyout(mpt_page.vaddr, page_req->buf, page_req->len); break; #ifdef __amd64__ case MPTIO_READ_EXT_CFG_HEADER32: #endif case MPTIO_READ_EXT_CFG_HEADER: MPT_LOCK(mpt); error = mpt_user_read_extcfg_header(mpt, ext_page_req); MPT_UNLOCK(mpt); break; #ifdef __amd64__ case MPTIO_READ_EXT_CFG_PAGE32: #endif case MPTIO_READ_EXT_CFG_PAGE: error = mpt_alloc_buffer(mpt, &mpt_page, ext_page_req->len); if (error) break; error = copyin(ext_page_req->buf, mpt_page.vaddr, sizeof(CONFIG_EXTENDED_PAGE_HEADER)); if (error) break; MPT_LOCK(mpt); error = mpt_user_read_extcfg_page(mpt, ext_page_req, &mpt_page); MPT_UNLOCK(mpt); if (error) break; error = copyout(mpt_page.vaddr, ext_page_req->buf, ext_page_req->len); break; #ifdef __amd64__ case MPTIO_WRITE_CFG_PAGE32: #endif case MPTIO_WRITE_CFG_PAGE: error = mpt_alloc_buffer(mpt, &mpt_page, page_req->len); if (error) break; error = copyin(page_req->buf, mpt_page.vaddr, page_req->len); if (error) break; MPT_LOCK(mpt); error = mpt_user_write_cfg_page(mpt, page_req, &mpt_page); MPT_UNLOCK(mpt); break; #ifdef __amd64__ case MPTIO_RAID_ACTION32: #endif case MPTIO_RAID_ACTION: if (raid_act->buf != NULL) { error = mpt_alloc_buffer(mpt, &mpt_page, raid_act->len); if (error) break; error = copyin(raid_act->buf, mpt_page.vaddr, raid_act->len); if (error) break; } MPT_LOCK(mpt); error = mpt_user_raid_action(mpt, raid_act, &mpt_page); MPT_UNLOCK(mpt); if (error) break; if (raid_act->buf != NULL) error = copyout(mpt_page.vaddr, raid_act->buf, raid_act->len); break; default: error = ENOIOCTL; break; } mpt_free_buffer(&mpt_page); if (error) return (error); #ifdef __amd64__ /* Convert native structs to 32-bit ones. */ switch (cmd) { case MPTIO_READ_CFG_HEADER32: case MPTIO_READ_CFG_PAGE32: case MPTIO_WRITE_CFG_PAGE32: page_req32->header = page_req->header; page_req32->page_address = page_req->page_address; page_req32->buf = PTROUT(page_req->buf); page_req32->len = page_req->len; page_req32->ioc_status = page_req->ioc_status; break; case MPTIO_READ_EXT_CFG_HEADER32: case MPTIO_READ_EXT_CFG_PAGE32: ext_page_req32->header = ext_page_req->header; ext_page_req32->page_address = ext_page_req->page_address; ext_page_req32->buf = PTROUT(ext_page_req->buf); ext_page_req32->len = ext_page_req->len; ext_page_req32->ioc_status = ext_page_req->ioc_status; break; case MPTIO_RAID_ACTION32: raid_act32->action = raid_act->action; raid_act32->volume_bus = raid_act->volume_bus; raid_act32->volume_id = raid_act->volume_id; raid_act32->phys_disk_num = raid_act->phys_disk_num; raid_act32->action_data_word = raid_act->action_data_word; raid_act32->buf = PTROUT(raid_act->buf); raid_act32->len = raid_act->len; raid_act32->volume_status = raid_act->volume_status; bcopy(raid_act->action_data, raid_act32->action_data, sizeof(raid_act->action_data)); raid_act32->action_status = raid_act->action_status; raid_act32->ioc_status = raid_act->ioc_status; raid_act32->write = raid_act->write; break; } #endif return (0); } Index: stable/12/sys/i386/linux/linux.h =================================================================== --- stable/12/sys/i386/linux/linux.h (revision 360445) +++ stable/12/sys/i386/linux/linux.h (revision 360446) @@ -1,606 +1,599 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1994-1996 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * $FreeBSD$ */ #ifndef _I386_LINUX_H_ #define _I386_LINUX_H_ +#include #include /* for sigval union */ #include #include #define LINUX_LEGACY_SYSCALLS /* * debugging support */ extern u_char linux_debug_map[]; #define ldebug(name) isclr(linux_debug_map, LINUX_SYS_linux_ ## name) #define ARGS(nm, fmt) "linux(%ld/%ld): "#nm"("fmt")\n", \ (long)td->td_proc->p_pid, (long)td->td_tid #define LMSG(fmt) "linux(%ld/%ld): "fmt"\n", \ (long)td->td_proc->p_pid, (long)td->td_tid #define LINUX_DTRACE linuxulator #define LINUX_SHAREDPAGE (VM_MAXUSER_ADDRESS - PAGE_SIZE) #define LINUX_USRSTACK LINUX_SHAREDPAGE - -#define PTRIN(v) (void *)(v) -#define PTROUT(v) (l_uintptr_t)(v) - -#define CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0) -#define CP2(src,dst,sfld,dfld) do { (dst).dfld = (src).sfld; } while (0) -#define PTRIN_CP(src,dst,fld) \ - do { (dst).fld = PTRIN((src).fld); } while (0) /* * Provide a separate set of types for the Linux types. */ typedef int l_int; typedef int32_t l_long; typedef int64_t l_longlong; typedef short l_short; typedef unsigned int l_uint; typedef uint32_t l_ulong; typedef uint64_t l_ulonglong; typedef unsigned short l_ushort; typedef char *l_caddr_t; typedef l_ulong l_uintptr_t; typedef l_long l_clock_t; typedef l_int l_daddr_t; typedef l_ushort l_dev_t; typedef l_uint l_gid_t; typedef l_ushort l_gid16_t; typedef l_ulong l_ino_t; typedef l_int l_key_t; typedef l_longlong l_loff_t; typedef l_ushort l_mode_t; typedef l_long l_off_t; typedef l_int l_pid_t; typedef l_uint l_size_t; typedef l_long l_suseconds_t; typedef l_long l_time_t; typedef l_uint l_uid_t; typedef l_ushort l_uid16_t; typedef l_int l_timer_t; typedef l_int l_mqd_t; typedef l_ulong l_fd_mask; typedef struct { l_int val[2]; } l_fsid_t; typedef struct { l_time_t tv_sec; l_suseconds_t tv_usec; } l_timeval; #define l_fd_set fd_set /* * Miscellaneous */ #define LINUX_AT_COUNT 20 /* Count of used aux entry types. * Keep this synchronized with * linux_fixup_elf() code. */ struct l___sysctl_args { l_int *name; l_int nlen; void *oldval; l_size_t *oldlenp; void *newval; l_size_t newlen; l_ulong __spare[4]; }; /* Resource limits */ #define LINUX_RLIMIT_CPU 0 #define LINUX_RLIMIT_FSIZE 1 #define LINUX_RLIMIT_DATA 2 #define LINUX_RLIMIT_STACK 3 #define LINUX_RLIMIT_CORE 4 #define LINUX_RLIMIT_RSS 5 #define LINUX_RLIMIT_NPROC 6 #define LINUX_RLIMIT_NOFILE 7 #define LINUX_RLIMIT_MEMLOCK 8 #define LINUX_RLIMIT_AS 9 /* Address space limit */ #define LINUX_RLIM_NLIMITS 10 struct l_rlimit { l_ulong rlim_cur; l_ulong rlim_max; }; struct l_mmap_argv { l_uintptr_t addr; l_size_t len; l_int prot; l_int flags; l_int fd; l_off_t pgoff; } __packed; /* * stat family of syscalls */ struct l_timespec { l_time_t tv_sec; l_long tv_nsec; }; struct l_newstat { l_ushort st_dev; l_ushort __pad1; l_ulong st_ino; l_ushort st_mode; l_ushort st_nlink; l_ushort st_uid; l_ushort st_gid; l_ushort st_rdev; l_ushort __pad2; l_ulong st_size; l_ulong st_blksize; l_ulong st_blocks; struct l_timespec st_atim; struct l_timespec st_mtim; struct l_timespec st_ctim; l_ulong __unused4; l_ulong __unused5; }; struct l_stat { l_ushort st_dev; l_ulong st_ino; l_ushort st_mode; l_ushort st_nlink; l_ushort st_uid; l_ushort st_gid; l_ushort st_rdev; l_long st_size; struct l_timespec st_atim; struct l_timespec st_mtim; struct l_timespec st_ctim; l_long st_blksize; l_long st_blocks; l_ulong st_flags; l_ulong st_gen; }; struct l_stat64 { l_ushort st_dev; u_char __pad0[10]; l_ulong __st_ino; l_uint st_mode; l_uint st_nlink; l_ulong st_uid; l_ulong st_gid; l_ushort st_rdev; u_char __pad3[10]; l_longlong st_size; l_ulong st_blksize; l_ulong st_blocks; l_ulong __pad4; struct l_timespec st_atim; struct l_timespec st_mtim; struct l_timespec st_ctim; l_ulonglong st_ino; }; struct l_statfs64 { l_int f_type; l_int f_bsize; uint64_t f_blocks; uint64_t f_bfree; uint64_t f_bavail; uint64_t f_files; uint64_t f_ffree; l_fsid_t f_fsid; l_int f_namelen; l_int f_frsize; l_int f_flags; l_int f_spare[4]; }; #define LINUX_NSIG_WORDS 2 /* sigaction flags */ #define LINUX_SA_NOCLDSTOP 0x00000001 #define LINUX_SA_NOCLDWAIT 0x00000002 #define LINUX_SA_SIGINFO 0x00000004 #define LINUX_SA_RESTORER 0x04000000 #define LINUX_SA_ONSTACK 0x08000000 #define LINUX_SA_RESTART 0x10000000 #define LINUX_SA_INTERRUPT 0x20000000 #define LINUX_SA_NOMASK 0x40000000 #define LINUX_SA_ONESHOT 0x80000000 /* sigprocmask actions */ #define LINUX_SIG_BLOCK 0 #define LINUX_SIG_UNBLOCK 1 #define LINUX_SIG_SETMASK 2 /* sigaltstack */ #define LINUX_MINSIGSTKSZ 2048 typedef void (*l_handler_t)(l_int); typedef l_ulong l_osigset_t; typedef struct { l_handler_t lsa_handler; l_osigset_t lsa_mask; l_ulong lsa_flags; void (*lsa_restorer)(void); } l_osigaction_t; typedef struct { l_handler_t lsa_handler; l_ulong lsa_flags; void (*lsa_restorer)(void); l_sigset_t lsa_mask; } l_sigaction_t; typedef struct { void *ss_sp; l_int ss_flags; l_size_t ss_size; } l_stack_t; /* The Linux sigcontext, pretty much a standard 386 trapframe. */ struct l_sigcontext { l_int sc_gs; l_int sc_fs; l_int sc_es; l_int sc_ds; l_int sc_edi; l_int sc_esi; l_int sc_ebp; l_int sc_esp; l_int sc_ebx; l_int sc_edx; l_int sc_ecx; l_int sc_eax; l_int sc_trapno; l_int sc_err; l_int sc_eip; l_int sc_cs; l_int sc_eflags; l_int sc_esp_at_signal; l_int sc_ss; l_int sc_387; l_int sc_mask; l_int sc_cr2; }; struct l_ucontext { l_ulong uc_flags; void *uc_link; l_stack_t uc_stack; struct l_sigcontext uc_mcontext; l_sigset_t uc_sigmask; }; #define LINUX_SI_MAX_SIZE 128 #define LINUX_SI_PAD_SIZE ((LINUX_SI_MAX_SIZE/sizeof(l_int)) - 3) typedef union l_sigval { l_int sival_int; l_uintptr_t sival_ptr; } l_sigval_t; typedef struct l_siginfo { l_int lsi_signo; l_int lsi_errno; l_int lsi_code; union { l_int _pad[LINUX_SI_PAD_SIZE]; struct { l_pid_t _pid; l_uid_t _uid; } _kill; struct { l_timer_t _tid; l_int _overrun; char _pad[sizeof(l_uid_t) - sizeof(l_int)]; l_sigval_t _sigval; l_int _sys_private; } _timer; struct { l_pid_t _pid; /* sender's pid */ l_uid_t _uid; /* sender's uid */ l_sigval_t _sigval; } _rt; struct { l_pid_t _pid; /* which child */ l_uid_t _uid; /* sender's uid */ l_int _status; /* exit code */ l_clock_t _utime; l_clock_t _stime; } _sigchld; struct { l_uintptr_t _addr; /* Faulting insn/memory ref. */ } _sigfault; struct { l_long _band; /* POLL_IN,POLL_OUT,POLL_MSG */ l_int _fd; } _sigpoll; } _sifields; } l_siginfo_t; #define lsi_pid _sifields._kill._pid #define lsi_uid _sifields._kill._uid #define lsi_tid _sifields._timer._tid #define lsi_overrun _sifields._timer._overrun #define lsi_sys_private _sifields._timer._sys_private #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.sival_int #define lsi_ptr _sifields._rt._sigval.sival_ptr #define lsi_addr _sifields._sigfault._addr #define lsi_band _sifields._sigpoll._band #define lsi_fd _sifields._sigpoll._fd struct l_fpreg { u_int16_t significand[4]; u_int16_t exponent; }; struct l_fpxreg { u_int16_t significand[4]; u_int16_t exponent; u_int16_t padding[3]; }; struct l_xmmreg { u_int32_t element[4]; }; struct l_fpstate { /* Regular FPU environment */ u_int32_t cw; u_int32_t sw; u_int32_t tag; u_int32_t ipoff; u_int32_t cssel; u_int32_t dataoff; u_int32_t datasel; struct l_fpreg _st[8]; u_int16_t status; u_int16_t magic; /* 0xffff = regular FPU data */ /* FXSR FPU environment */ u_int32_t _fxsr_env[6]; /* env is ignored. */ u_int32_t mxcsr; u_int32_t reserved; struct l_fpxreg _fxsr_st[8]; /* reg data is ignored. */ struct l_xmmreg _xmm[8]; u_int32_t padding[56]; }; /* * We make the stack look like Linux expects it when calling a signal * handler, but use the BSD way of calling the handler and sigreturn(). * This means that we need to pass the pointer to the handler too. * It is appended to the frame to not interfere with the rest of it. */ struct l_sigframe { l_int sf_sig; struct l_sigcontext sf_sc; struct l_fpstate sf_fpstate; l_uint sf_extramask[LINUX_NSIG_WORDS-1]; l_handler_t sf_handler; }; struct l_rt_sigframe { l_int sf_sig; l_siginfo_t *sf_siginfo; struct l_ucontext *sf_ucontext; l_siginfo_t sf_si; struct l_ucontext sf_sc; l_handler_t sf_handler; }; extern struct sysentvec linux_sysvec; /* * arch specific open/fcntl flags */ #define LINUX_F_GETLK64 12 #define LINUX_F_SETLK64 13 #define LINUX_F_SETLKW64 14 union l_semun { l_int val; l_uintptr_t buf; l_ushort *array; l_uintptr_t __buf; l_uintptr_t __pad; }; struct l_ifmap { l_ulong mem_start; l_ulong mem_end; l_ushort base_addr; u_char irq; u_char dma; u_char port; }; struct l_ifreq { union { char ifrn_name[LINUX_IFNAMSIZ]; } ifr_ifrn; union { struct l_sockaddr ifru_addr; struct l_sockaddr ifru_dstaddr; struct l_sockaddr ifru_broadaddr; struct l_sockaddr ifru_netmask; struct l_sockaddr ifru_hwaddr; l_short ifru_flags[1]; l_int ifru_ivalue; l_int ifru_mtu; struct l_ifmap ifru_map; char ifru_slave[LINUX_IFNAMSIZ]; l_caddr_t ifru_data; } ifr_ifru; }; #define ifr_name ifr_ifrn.ifrn_name /* Interface name */ #define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ #define ifr_ifindex ifr_ifru.ifru_ivalue /* Interface index */ /* * poll() */ #define LINUX_POLLIN 0x0001 #define LINUX_POLLPRI 0x0002 #define LINUX_POLLOUT 0x0004 #define LINUX_POLLERR 0x0008 #define LINUX_POLLHUP 0x0010 #define LINUX_POLLNVAL 0x0020 #define LINUX_POLLRDNORM 0x0040 #define LINUX_POLLRDBAND 0x0080 #define LINUX_POLLWRNORM 0x0100 #define LINUX_POLLWRBAND 0x0200 #define LINUX_POLLMSG 0x0400 struct l_pollfd { l_int fd; l_short events; l_short revents; }; struct l_user_desc { l_uint entry_number; l_uint base_addr; l_uint limit; l_uint seg_32bit:1; l_uint contents:2; l_uint read_exec_only:1; l_uint limit_in_pages:1; l_uint seg_not_present:1; l_uint useable:1; }; struct l_desc_struct { unsigned long a, b; }; #define LINUX_LOWERWORD 0x0000ffff /* * Macros which does the same thing as those in Linux include/asm-um/ldt-i386.h. * These convert Linux user space descriptor to machine one. */ #define LINUX_LDT_entry_a(info) \ ((((info)->base_addr & LINUX_LOWERWORD) << 16) | \ ((info)->limit & LINUX_LOWERWORD)) #define LINUX_ENTRY_B_READ_EXEC_ONLY 9 #define LINUX_ENTRY_B_CONTENTS 10 #define LINUX_ENTRY_B_SEG_NOT_PRESENT 15 #define LINUX_ENTRY_B_BASE_ADDR 16 #define LINUX_ENTRY_B_USEABLE 20 #define LINUX_ENTRY_B_SEG32BIT 22 #define LINUX_ENTRY_B_LIMIT 23 #define LINUX_LDT_entry_b(info) \ (((info)->base_addr & 0xff000000) | \ ((info)->limit & 0xf0000) | \ ((info)->contents << LINUX_ENTRY_B_CONTENTS) | \ (((info)->seg_not_present == 0) << LINUX_ENTRY_B_SEG_NOT_PRESENT) | \ (((info)->base_addr & 0x00ff0000) >> LINUX_ENTRY_B_BASE_ADDR) | \ (((info)->read_exec_only == 0) << LINUX_ENTRY_B_READ_EXEC_ONLY) | \ ((info)->seg_32bit << LINUX_ENTRY_B_SEG32BIT) | \ ((info)->useable << LINUX_ENTRY_B_USEABLE) | \ ((info)->limit_in_pages << LINUX_ENTRY_B_LIMIT) | 0x7000) #define LINUX_LDT_empty(info) \ ((info)->base_addr == 0 && \ (info)->limit == 0 && \ (info)->contents == 0 && \ (info)->seg_not_present == 1 && \ (info)->read_exec_only == 1 && \ (info)->seg_32bit == 0 && \ (info)->limit_in_pages == 0 && \ (info)->useable == 0) /* * Macros for converting segments. * They do the same as those in arch/i386/kernel/process.c in Linux. */ #define LINUX_GET_BASE(desc) \ ((((desc)->a >> 16) & LINUX_LOWERWORD) | \ (((desc)->b << 16) & 0x00ff0000) | \ ((desc)->b & 0xff000000)) #define LINUX_GET_LIMIT(desc) \ (((desc)->a & LINUX_LOWERWORD) | \ ((desc)->b & 0xf0000)) #define LINUX_GET_32BIT(desc) \ (((desc)->b >> LINUX_ENTRY_B_SEG32BIT) & 1) #define LINUX_GET_CONTENTS(desc) \ (((desc)->b >> LINUX_ENTRY_B_CONTENTS) & 3) #define LINUX_GET_WRITABLE(desc) \ (((desc)->b >> LINUX_ENTRY_B_READ_EXEC_ONLY) & 1) #define LINUX_GET_LIMIT_PAGES(desc) \ (((desc)->b >> LINUX_ENTRY_B_LIMIT) & 1) #define LINUX_GET_PRESENT(desc) \ (((desc)->b >> LINUX_ENTRY_B_SEG_NOT_PRESENT) & 1) #define LINUX_GET_USEABLE(desc) \ (((desc)->b >> LINUX_ENTRY_B_USEABLE) & 1) #define linux_copyout_rusage(r, u) copyout(r, u, sizeof(*r)) /* robust futexes */ struct linux_robust_list { struct linux_robust_list *next; }; struct linux_robust_list_head { struct linux_robust_list list; l_long futex_offset; struct linux_robust_list *pending_list; }; #endif /* !_I386_LINUX_H_ */ Index: stable/12/sys/kern/sysv_sem.c =================================================================== --- stable/12/sys/kern/sysv_sem.c (revision 360445) +++ stable/12/sys/kern/sysv_sem.c (revision 360446) @@ -1,2007 +1,2004 @@ /*- * Implementation of SVID semaphores * * Author: Daniel Boulet * * This software is provided ``AS IS'' without any warranties of any kind. */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2003-2005 McAfee, Inc. * Copyright (c) 2016-2017 Robert N. M. Watson * All rights reserved. * * This software was developed for the FreeBSD Project in part by McAfee * Research, the Security Research Division of McAfee, Inc under DARPA/SPAWAR * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS research * program. * * Portions of this software were developed by BAE Systems, the University of * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent * Computing (TC) research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_sysvipc.h" #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include FEATURE(sysv_sem, "System V semaphores support"); static MALLOC_DEFINE(M_SEM, "sem", "SVID compatible semaphores"); #ifdef SEM_DEBUG #define DPRINTF(a) printf a #else #define DPRINTF(a) #endif static int seminit(void); static int sysvsem_modload(struct module *, int, void *); static int semunload(void); static void semexit_myhook(void *arg, struct proc *p); static int sysctl_sema(SYSCTL_HANDLER_ARGS); static int semvalid(int semid, struct prison *rpr, struct semid_kernel *semakptr); static void sem_remove(int semidx, struct ucred *cred); static struct prison *sem_find_prison(struct ucred *); static int sem_prison_cansee(struct prison *, struct semid_kernel *); static int sem_prison_check(void *, void *); static int sem_prison_set(void *, void *); static int sem_prison_get(void *, void *); static int sem_prison_remove(void *, void *); static void sem_prison_cleanup(struct prison *); #ifndef _SYS_SYSPROTO_H_ struct __semctl_args; int __semctl(struct thread *td, struct __semctl_args *uap); struct semget_args; int semget(struct thread *td, struct semget_args *uap); struct semop_args; int semop(struct thread *td, struct semop_args *uap); #endif static struct sem_undo *semu_alloc(struct thread *td); static int semundo_adjust(struct thread *td, struct sem_undo **supptr, int semid, int semseq, int semnum, int adjval); static void semundo_clear(int semid, int semnum); static struct mtx sem_mtx; /* semaphore global lock */ static struct mtx sem_undo_mtx; static int semtot = 0; static struct semid_kernel *sema; /* semaphore id pool */ static struct mtx *sema_mtx; /* semaphore id pool mutexes*/ static struct sem *sem; /* semaphore pool */ LIST_HEAD(, sem_undo) semu_list; /* list of active undo structures */ LIST_HEAD(, sem_undo) semu_free_list; /* list of free undo structures */ static int *semu; /* undo structure pool */ static eventhandler_tag semexit_tag; static unsigned sem_prison_slot; /* prison OSD slot */ #define SEMUNDO_MTX sem_undo_mtx #define SEMUNDO_LOCK() mtx_lock(&SEMUNDO_MTX); #define SEMUNDO_UNLOCK() mtx_unlock(&SEMUNDO_MTX); #define SEMUNDO_LOCKASSERT(how) mtx_assert(&SEMUNDO_MTX, (how)); struct sem { u_short semval; /* semaphore value */ pid_t sempid; /* pid of last operation */ u_short semncnt; /* # awaiting semval > cval */ u_short semzcnt; /* # awaiting semval = 0 */ }; /* * Undo structure (one per process) */ struct sem_undo { LIST_ENTRY(sem_undo) un_next; /* ptr to next active undo structure */ struct proc *un_proc; /* owner of this structure */ short un_cnt; /* # of active entries */ struct undo { short un_adjval; /* adjust on exit values */ short un_num; /* semaphore # */ int un_id; /* semid */ unsigned short un_seq; } un_ent[1]; /* undo entries */ }; /* * Configuration parameters */ #ifndef SEMMNI #define SEMMNI 50 /* # of semaphore identifiers */ #endif #ifndef SEMMNS #define SEMMNS 340 /* # of semaphores in system */ #endif #ifndef SEMUME #define SEMUME 50 /* max # of undo entries per process */ #endif #ifndef SEMMNU #define SEMMNU 150 /* # of undo structures in system */ #endif /* shouldn't need tuning */ #ifndef SEMMSL #define SEMMSL SEMMNS /* max # of semaphores per id */ #endif #ifndef SEMOPM #define SEMOPM 100 /* max # of operations per semop call */ #endif #define SEMVMX 32767 /* semaphore maximum value */ #define SEMAEM 16384 /* adjust on exit max value */ /* * Due to the way semaphore memory is allocated, we have to ensure that * SEMUSZ is properly aligned. */ #define SEM_ALIGN(bytes) roundup2(bytes, sizeof(long)) /* actual size of an undo structure */ #define SEMUSZ SEM_ALIGN(offsetof(struct sem_undo, un_ent[SEMUME])) /* * Macro to find a particular sem_undo vector */ #define SEMU(ix) \ ((struct sem_undo *)(((intptr_t)semu)+ix * seminfo.semusz)) /* * semaphore info struct */ struct seminfo seminfo = { .semmni = SEMMNI, /* # of semaphore identifiers */ .semmns = SEMMNS, /* # of semaphores in system */ .semmnu = SEMMNU, /* # of undo structures in system */ .semmsl = SEMMSL, /* max # of semaphores per id */ .semopm = SEMOPM, /* max # of operations per semop call */ .semume = SEMUME, /* max # of undo entries per process */ .semusz = SEMUSZ, /* size in bytes of undo structure */ .semvmx = SEMVMX, /* semaphore maximum value */ .semaem = SEMAEM, /* adjust on exit max value */ }; SYSCTL_INT(_kern_ipc, OID_AUTO, semmni, CTLFLAG_RDTUN, &seminfo.semmni, 0, "Number of semaphore identifiers"); SYSCTL_INT(_kern_ipc, OID_AUTO, semmns, CTLFLAG_RDTUN, &seminfo.semmns, 0, "Maximum number of semaphores in the system"); SYSCTL_INT(_kern_ipc, OID_AUTO, semmnu, CTLFLAG_RDTUN, &seminfo.semmnu, 0, "Maximum number of undo structures in the system"); SYSCTL_INT(_kern_ipc, OID_AUTO, semmsl, CTLFLAG_RWTUN, &seminfo.semmsl, 0, "Max semaphores per id"); SYSCTL_INT(_kern_ipc, OID_AUTO, semopm, CTLFLAG_RDTUN, &seminfo.semopm, 0, "Max operations per semop call"); SYSCTL_INT(_kern_ipc, OID_AUTO, semume, CTLFLAG_RDTUN, &seminfo.semume, 0, "Max undo entries per process"); SYSCTL_INT(_kern_ipc, OID_AUTO, semusz, CTLFLAG_RDTUN, &seminfo.semusz, 0, "Size in bytes of undo structure"); SYSCTL_INT(_kern_ipc, OID_AUTO, semvmx, CTLFLAG_RWTUN, &seminfo.semvmx, 0, "Semaphore maximum value"); SYSCTL_INT(_kern_ipc, OID_AUTO, semaem, CTLFLAG_RWTUN, &seminfo.semaem, 0, "Adjust on exit max value"); SYSCTL_PROC(_kern_ipc, OID_AUTO, sema, CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, sysctl_sema, "", "Array of struct semid_kernel for each potential semaphore"); static struct syscall_helper_data sem_syscalls[] = { SYSCALL_INIT_HELPER(__semctl), SYSCALL_INIT_HELPER(semget), SYSCALL_INIT_HELPER(semop), #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) SYSCALL_INIT_HELPER(semsys), SYSCALL_INIT_HELPER_COMPAT(freebsd7___semctl), #endif SYSCALL_INIT_LAST }; #ifdef COMPAT_FREEBSD32 #include #include #include #include #include #include static struct syscall_helper_data sem32_syscalls[] = { SYSCALL32_INIT_HELPER(freebsd32_semctl), SYSCALL32_INIT_HELPER_COMPAT(semget), SYSCALL32_INIT_HELPER_COMPAT(semop), SYSCALL32_INIT_HELPER(freebsd32_semsys), #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) SYSCALL32_INIT_HELPER(freebsd7_freebsd32_semctl), #endif SYSCALL_INIT_LAST }; #endif static int seminit(void) { struct prison *pr; void **rsv; int i, error; osd_method_t methods[PR_MAXMETHOD] = { [PR_METHOD_CHECK] = sem_prison_check, [PR_METHOD_SET] = sem_prison_set, [PR_METHOD_GET] = sem_prison_get, [PR_METHOD_REMOVE] = sem_prison_remove, }; sem = malloc(sizeof(struct sem) * seminfo.semmns, M_SEM, M_WAITOK); sema = malloc(sizeof(struct semid_kernel) * seminfo.semmni, M_SEM, M_WAITOK | M_ZERO); sema_mtx = malloc(sizeof(struct mtx) * seminfo.semmni, M_SEM, M_WAITOK | M_ZERO); semu = malloc(seminfo.semmnu * seminfo.semusz, M_SEM, M_WAITOK); for (i = 0; i < seminfo.semmni; i++) { sema[i].u.__sem_base = 0; sema[i].u.sem_perm.mode = 0; sema[i].u.sem_perm.seq = 0; #ifdef MAC mac_sysvsem_init(&sema[i]); #endif } for (i = 0; i < seminfo.semmni; i++) mtx_init(&sema_mtx[i], "semid", NULL, MTX_DEF); LIST_INIT(&semu_free_list); for (i = 0; i < seminfo.semmnu; i++) { struct sem_undo *suptr = SEMU(i); suptr->un_proc = NULL; LIST_INSERT_HEAD(&semu_free_list, suptr, un_next); } LIST_INIT(&semu_list); mtx_init(&sem_mtx, "sem", NULL, MTX_DEF); mtx_init(&sem_undo_mtx, "semu", NULL, MTX_DEF); semexit_tag = EVENTHANDLER_REGISTER(process_exit, semexit_myhook, NULL, EVENTHANDLER_PRI_ANY); /* Set current prisons according to their allow.sysvipc. */ sem_prison_slot = osd_jail_register(NULL, methods); rsv = osd_reserve(sem_prison_slot); prison_lock(&prison0); (void)osd_jail_set_reserved(&prison0, sem_prison_slot, rsv, &prison0); prison_unlock(&prison0); rsv = NULL; sx_slock(&allprison_lock); TAILQ_FOREACH(pr, &allprison, pr_list) { if (rsv == NULL) rsv = osd_reserve(sem_prison_slot); prison_lock(pr); if ((pr->pr_allow & PR_ALLOW_SYSVIPC) && pr->pr_ref > 0) { (void)osd_jail_set_reserved(pr, sem_prison_slot, rsv, &prison0); rsv = NULL; } prison_unlock(pr); } if (rsv != NULL) osd_free_reserved(rsv); sx_sunlock(&allprison_lock); error = syscall_helper_register(sem_syscalls, SY_THR_STATIC_KLD); if (error != 0) return (error); #ifdef COMPAT_FREEBSD32 error = syscall32_helper_register(sem32_syscalls, SY_THR_STATIC_KLD); if (error != 0) return (error); #endif return (0); } static int semunload(void) { int i; /* XXXKIB */ if (semtot != 0) return (EBUSY); #ifdef COMPAT_FREEBSD32 syscall32_helper_unregister(sem32_syscalls); #endif syscall_helper_unregister(sem_syscalls); EVENTHANDLER_DEREGISTER(process_exit, semexit_tag); if (sem_prison_slot != 0) osd_jail_deregister(sem_prison_slot); #ifdef MAC for (i = 0; i < seminfo.semmni; i++) mac_sysvsem_destroy(&sema[i]); #endif free(sem, M_SEM); free(sema, M_SEM); free(semu, M_SEM); for (i = 0; i < seminfo.semmni; i++) mtx_destroy(&sema_mtx[i]); free(sema_mtx, M_SEM); mtx_destroy(&sem_mtx); mtx_destroy(&sem_undo_mtx); return (0); } static int sysvsem_modload(struct module *module, int cmd, void *arg) { int error = 0; switch (cmd) { case MOD_LOAD: error = seminit(); if (error != 0) semunload(); break; case MOD_UNLOAD: error = semunload(); break; case MOD_SHUTDOWN: break; default: error = EINVAL; break; } return (error); } static moduledata_t sysvsem_mod = { "sysvsem", &sysvsem_modload, NULL }; DECLARE_MODULE(sysvsem, sysvsem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST); MODULE_VERSION(sysvsem, 1); /* * Allocate a new sem_undo structure for a process * (returns ptr to structure or NULL if no more room) */ static struct sem_undo * semu_alloc(struct thread *td) { struct sem_undo *suptr; SEMUNDO_LOCKASSERT(MA_OWNED); if ((suptr = LIST_FIRST(&semu_free_list)) == NULL) return (NULL); LIST_REMOVE(suptr, un_next); LIST_INSERT_HEAD(&semu_list, suptr, un_next); suptr->un_cnt = 0; suptr->un_proc = td->td_proc; return (suptr); } static int semu_try_free(struct sem_undo *suptr) { SEMUNDO_LOCKASSERT(MA_OWNED); if (suptr->un_cnt != 0) return (0); LIST_REMOVE(suptr, un_next); LIST_INSERT_HEAD(&semu_free_list, suptr, un_next); return (1); } /* * Adjust a particular entry for a particular proc */ static int semundo_adjust(struct thread *td, struct sem_undo **supptr, int semid, int semseq, int semnum, int adjval) { struct proc *p = td->td_proc; struct sem_undo *suptr; struct undo *sunptr; int i; SEMUNDO_LOCKASSERT(MA_OWNED); /* Look for and remember the sem_undo if the caller doesn't provide it */ suptr = *supptr; if (suptr == NULL) { LIST_FOREACH(suptr, &semu_list, un_next) { if (suptr->un_proc == p) { *supptr = suptr; break; } } if (suptr == NULL) { if (adjval == 0) return(0); suptr = semu_alloc(td); if (suptr == NULL) return (ENOSPC); *supptr = suptr; } } /* * Look for the requested entry and adjust it (delete if adjval becomes * 0). */ sunptr = &suptr->un_ent[0]; for (i = 0; i < suptr->un_cnt; i++, sunptr++) { if (sunptr->un_id != semid || sunptr->un_num != semnum) continue; if (adjval != 0) { adjval += sunptr->un_adjval; if (adjval > seminfo.semaem || adjval < -seminfo.semaem) return (ERANGE); } sunptr->un_adjval = adjval; if (sunptr->un_adjval == 0) { suptr->un_cnt--; if (i < suptr->un_cnt) suptr->un_ent[i] = suptr->un_ent[suptr->un_cnt]; if (suptr->un_cnt == 0) semu_try_free(suptr); } return (0); } /* Didn't find the right entry - create it */ if (adjval == 0) return (0); if (adjval > seminfo.semaem || adjval < -seminfo.semaem) return (ERANGE); if (suptr->un_cnt != seminfo.semume) { sunptr = &suptr->un_ent[suptr->un_cnt]; suptr->un_cnt++; sunptr->un_adjval = adjval; sunptr->un_id = semid; sunptr->un_num = semnum; sunptr->un_seq = semseq; } else return (EINVAL); return (0); } static void semundo_clear(int semid, int semnum) { struct sem_undo *suptr, *suptr1; struct undo *sunptr; int i; SEMUNDO_LOCKASSERT(MA_OWNED); LIST_FOREACH_SAFE(suptr, &semu_list, un_next, suptr1) { sunptr = &suptr->un_ent[0]; for (i = 0; i < suptr->un_cnt; i++, sunptr++) { if (sunptr->un_id != semid) continue; if (semnum == -1 || sunptr->un_num == semnum) { suptr->un_cnt--; if (i < suptr->un_cnt) { suptr->un_ent[i] = suptr->un_ent[suptr->un_cnt]; continue; } semu_try_free(suptr); } if (semnum != -1) break; } } } static int semvalid(int semid, struct prison *rpr, struct semid_kernel *semakptr) { return ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 || semakptr->u.sem_perm.seq != IPCID_TO_SEQ(semid) || sem_prison_cansee(rpr, semakptr) ? EINVAL : 0); } static void sem_remove(int semidx, struct ucred *cred) { struct semid_kernel *semakptr; int i; KASSERT(semidx >= 0 && semidx < seminfo.semmni, ("semidx out of bounds")); mtx_assert(&sem_mtx, MA_OWNED); semakptr = &sema[semidx]; KASSERT(semakptr->u.__sem_base - sem + semakptr->u.sem_nsems <= semtot, ("sem_remove: sema %d corrupted sem pointer %p %p %d %d", semidx, semakptr->u.__sem_base, sem, semakptr->u.sem_nsems, semtot)); semakptr->u.sem_perm.cuid = cred ? cred->cr_uid : 0; semakptr->u.sem_perm.uid = cred ? cred->cr_uid : 0; semakptr->u.sem_perm.mode = 0; racct_sub_cred(semakptr->cred, RACCT_NSEM, semakptr->u.sem_nsems); crfree(semakptr->cred); semakptr->cred = NULL; SEMUNDO_LOCK(); semundo_clear(semidx, -1); SEMUNDO_UNLOCK(); #ifdef MAC mac_sysvsem_cleanup(semakptr); #endif wakeup(semakptr); for (i = 0; i < seminfo.semmni; i++) { if ((sema[i].u.sem_perm.mode & SEM_ALLOC) && sema[i].u.__sem_base > semakptr->u.__sem_base) mtx_lock_flags(&sema_mtx[i], LOP_DUPOK); } for (i = semakptr->u.__sem_base - sem + semakptr->u.sem_nsems; i < semtot; i++) sem[i - semakptr->u.sem_nsems] = sem[i]; for (i = 0; i < seminfo.semmni; i++) { if ((sema[i].u.sem_perm.mode & SEM_ALLOC) && sema[i].u.__sem_base > semakptr->u.__sem_base) { sema[i].u.__sem_base -= semakptr->u.sem_nsems; mtx_unlock(&sema_mtx[i]); } } semtot -= semakptr->u.sem_nsems; } static struct prison * sem_find_prison(struct ucred *cred) { struct prison *pr, *rpr; pr = cred->cr_prison; prison_lock(pr); rpr = osd_jail_get(pr, sem_prison_slot); prison_unlock(pr); return rpr; } static int sem_prison_cansee(struct prison *rpr, struct semid_kernel *semakptr) { if (semakptr->cred == NULL || !(rpr == semakptr->cred->cr_prison || prison_ischild(rpr, semakptr->cred->cr_prison))) return (EINVAL); return (0); } /* * Note that the user-mode half of this passes a union, not a pointer. */ #ifndef _SYS_SYSPROTO_H_ struct __semctl_args { int semid; int semnum; int cmd; union semun *arg; }; #endif int sys___semctl(struct thread *td, struct __semctl_args *uap) { struct semid_ds dsbuf; union semun arg, semun; register_t rval; int error; switch (uap->cmd) { case SEM_STAT: case IPC_SET: case IPC_STAT: case GETALL: case SETVAL: case SETALL: error = copyin(uap->arg, &arg, sizeof(arg)); if (error) return (error); break; } switch (uap->cmd) { case SEM_STAT: case IPC_STAT: semun.buf = &dsbuf; break; case IPC_SET: error = copyin(arg.buf, &dsbuf, sizeof(dsbuf)); if (error) return (error); semun.buf = &dsbuf; break; case GETALL: case SETALL: semun.array = arg.array; break; case SETVAL: semun.val = arg.val; break; } error = kern_semctl(td, uap->semid, uap->semnum, uap->cmd, &semun, &rval); if (error) return (error); switch (uap->cmd) { case SEM_STAT: case IPC_STAT: error = copyout(&dsbuf, arg.buf, sizeof(dsbuf)); break; } if (error == 0) td->td_retval[0] = rval; return (error); } int kern_semctl(struct thread *td, int semid, int semnum, int cmd, union semun *arg, register_t *rval) { u_short *array; struct ucred *cred = td->td_ucred; int i, error; struct prison *rpr; struct semid_ds *sbuf; struct semid_kernel *semakptr; struct mtx *sema_mtxp; u_short usval, count; int semidx; DPRINTF(("call to semctl(%d, %d, %d, 0x%p)\n", semid, semnum, cmd, arg)); AUDIT_ARG_SVIPC_CMD(cmd); AUDIT_ARG_SVIPC_ID(semid); rpr = sem_find_prison(td->td_ucred); if (sem == NULL) return (ENOSYS); array = NULL; switch(cmd) { case SEM_STAT: /* * For this command we assume semid is an array index * rather than an IPC id. */ if (semid < 0 || semid >= seminfo.semmni) return (EINVAL); semakptr = &sema[semid]; sema_mtxp = &sema_mtx[semid]; mtx_lock(sema_mtxp); if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0) { error = EINVAL; goto done2; } if ((error = sem_prison_cansee(rpr, semakptr))) goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R))) goto done2; #ifdef MAC error = mac_sysvsem_check_semctl(cred, semakptr, cmd); if (error != 0) goto done2; #endif bcopy(&semakptr->u, arg->buf, sizeof(struct semid_ds)); if (cred->cr_prison != semakptr->cred->cr_prison) arg->buf->sem_perm.key = IPC_PRIVATE; *rval = IXSEQ_TO_IPCID(semid, semakptr->u.sem_perm); mtx_unlock(sema_mtxp); return (0); } semidx = IPCID_TO_IX(semid); if (semidx < 0 || semidx >= seminfo.semmni) return (EINVAL); semakptr = &sema[semidx]; sema_mtxp = &sema_mtx[semidx]; if (cmd == IPC_RMID) mtx_lock(&sem_mtx); mtx_lock(sema_mtxp); #ifdef MAC error = mac_sysvsem_check_semctl(cred, semakptr, cmd); if (error != 0) goto done2; #endif error = 0; *rval = 0; switch (cmd) { case IPC_RMID: if ((error = semvalid(semid, rpr, semakptr)) != 0) goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_M))) goto done2; sem_remove(semidx, cred); break; case IPC_SET: AUDIT_ARG_SVIPC_PERM(&arg->buf->sem_perm); if ((error = semvalid(semid, rpr, semakptr)) != 0) goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_M))) goto done2; sbuf = arg->buf; semakptr->u.sem_perm.uid = sbuf->sem_perm.uid; semakptr->u.sem_perm.gid = sbuf->sem_perm.gid; semakptr->u.sem_perm.mode = (semakptr->u.sem_perm.mode & ~0777) | (sbuf->sem_perm.mode & 0777); semakptr->u.sem_ctime = time_second; break; case IPC_STAT: if ((error = semvalid(semid, rpr, semakptr)) != 0) goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R))) goto done2; bcopy(&semakptr->u, arg->buf, sizeof(struct semid_ds)); if (cred->cr_prison != semakptr->cred->cr_prison) arg->buf->sem_perm.key = IPC_PRIVATE; break; case GETNCNT: if ((error = semvalid(semid, rpr, semakptr)) != 0) goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R))) goto done2; if (semnum < 0 || semnum >= semakptr->u.sem_nsems) { error = EINVAL; goto done2; } *rval = semakptr->u.__sem_base[semnum].semncnt; break; case GETPID: if ((error = semvalid(semid, rpr, semakptr)) != 0) goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R))) goto done2; if (semnum < 0 || semnum >= semakptr->u.sem_nsems) { error = EINVAL; goto done2; } *rval = semakptr->u.__sem_base[semnum].sempid; break; case GETVAL: if ((error = semvalid(semid, rpr, semakptr)) != 0) goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R))) goto done2; if (semnum < 0 || semnum >= semakptr->u.sem_nsems) { error = EINVAL; goto done2; } *rval = semakptr->u.__sem_base[semnum].semval; break; case GETALL: /* * Unfortunately, callers of this function don't know * in advance how many semaphores are in this set. * While we could just allocate the maximum size array * and pass the actual size back to the caller, that * won't work for SETALL since we can't copyin() more * data than the user specified as we may return a * spurious EFAULT. * * Note that the number of semaphores in a set is * fixed for the life of that set. The only way that * the 'count' could change while are blocked in * malloc() is if this semaphore set were destroyed * and a new one created with the same index. * However, semvalid() will catch that due to the * sequence number unless exactly 0x8000 (or a * multiple thereof) semaphore sets for the same index * are created and destroyed while we are in malloc! * */ count = semakptr->u.sem_nsems; mtx_unlock(sema_mtxp); array = malloc(sizeof(*array) * count, M_TEMP, M_WAITOK); mtx_lock(sema_mtxp); if ((error = semvalid(semid, rpr, semakptr)) != 0) goto done2; KASSERT(count == semakptr->u.sem_nsems, ("nsems changed")); if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R))) goto done2; for (i = 0; i < semakptr->u.sem_nsems; i++) array[i] = semakptr->u.__sem_base[i].semval; mtx_unlock(sema_mtxp); error = copyout(array, arg->array, count * sizeof(*array)); mtx_lock(sema_mtxp); break; case GETZCNT: if ((error = semvalid(semid, rpr, semakptr)) != 0) goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R))) goto done2; if (semnum < 0 || semnum >= semakptr->u.sem_nsems) { error = EINVAL; goto done2; } *rval = semakptr->u.__sem_base[semnum].semzcnt; break; case SETVAL: if ((error = semvalid(semid, rpr, semakptr)) != 0) goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_W))) goto done2; if (semnum < 0 || semnum >= semakptr->u.sem_nsems) { error = EINVAL; goto done2; } if (arg->val < 0 || arg->val > seminfo.semvmx) { error = ERANGE; goto done2; } semakptr->u.__sem_base[semnum].semval = arg->val; SEMUNDO_LOCK(); semundo_clear(semidx, semnum); SEMUNDO_UNLOCK(); wakeup(semakptr); break; case SETALL: /* * See comment on GETALL for why 'count' shouldn't change * and why we require a userland buffer. */ count = semakptr->u.sem_nsems; mtx_unlock(sema_mtxp); array = malloc(sizeof(*array) * count, M_TEMP, M_WAITOK); error = copyin(arg->array, array, count * sizeof(*array)); mtx_lock(sema_mtxp); if (error) break; if ((error = semvalid(semid, rpr, semakptr)) != 0) goto done2; KASSERT(count == semakptr->u.sem_nsems, ("nsems changed")); if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_W))) goto done2; for (i = 0; i < semakptr->u.sem_nsems; i++) { usval = array[i]; if (usval > seminfo.semvmx) { error = ERANGE; break; } semakptr->u.__sem_base[i].semval = usval; } SEMUNDO_LOCK(); semundo_clear(semidx, -1); SEMUNDO_UNLOCK(); wakeup(semakptr); break; default: error = EINVAL; break; } done2: mtx_unlock(sema_mtxp); if (cmd == IPC_RMID) mtx_unlock(&sem_mtx); if (array != NULL) free(array, M_TEMP); return(error); } #ifndef _SYS_SYSPROTO_H_ struct semget_args { key_t key; int nsems; int semflg; }; #endif int sys_semget(struct thread *td, struct semget_args *uap) { int semid, error = 0; int key = uap->key; int nsems = uap->nsems; int semflg = uap->semflg; struct ucred *cred = td->td_ucred; DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg)); AUDIT_ARG_VALUE(semflg); if (sem_find_prison(cred) == NULL) return (ENOSYS); mtx_lock(&sem_mtx); if (key != IPC_PRIVATE) { for (semid = 0; semid < seminfo.semmni; semid++) { if ((sema[semid].u.sem_perm.mode & SEM_ALLOC) && sema[semid].cred != NULL && sema[semid].cred->cr_prison == cred->cr_prison && sema[semid].u.sem_perm.key == key) break; } if (semid < seminfo.semmni) { AUDIT_ARG_SVIPC_ID(semid); DPRINTF(("found public key\n")); if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) { DPRINTF(("not exclusive\n")); error = EEXIST; goto done2; } if ((error = ipcperm(td, &sema[semid].u.sem_perm, semflg & 0700))) { goto done2; } if (nsems > 0 && sema[semid].u.sem_nsems < nsems) { DPRINTF(("too small\n")); error = EINVAL; goto done2; } #ifdef MAC error = mac_sysvsem_check_semget(cred, &sema[semid]); if (error != 0) goto done2; #endif goto found; } } DPRINTF(("need to allocate the semid_kernel\n")); if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) { if (nsems <= 0 || nsems > seminfo.semmsl) { DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems, seminfo.semmsl)); error = EINVAL; goto done2; } if (nsems > seminfo.semmns - semtot) { DPRINTF(( "not enough semaphores left (need %d, got %d)\n", nsems, seminfo.semmns - semtot)); error = ENOSPC; goto done2; } for (semid = 0; semid < seminfo.semmni; semid++) { if ((sema[semid].u.sem_perm.mode & SEM_ALLOC) == 0) break; } if (semid == seminfo.semmni) { DPRINTF(("no more semid_kernel's available\n")); error = ENOSPC; goto done2; } #ifdef RACCT if (racct_enable) { PROC_LOCK(td->td_proc); error = racct_add(td->td_proc, RACCT_NSEM, nsems); PROC_UNLOCK(td->td_proc); if (error != 0) { error = ENOSPC; goto done2; } } #endif DPRINTF(("semid %d is available\n", semid)); mtx_lock(&sema_mtx[semid]); KASSERT((sema[semid].u.sem_perm.mode & SEM_ALLOC) == 0, ("Lost semaphore %d", semid)); sema[semid].u.sem_perm.key = key; sema[semid].u.sem_perm.cuid = cred->cr_uid; sema[semid].u.sem_perm.uid = cred->cr_uid; sema[semid].u.sem_perm.cgid = cred->cr_gid; sema[semid].u.sem_perm.gid = cred->cr_gid; sema[semid].u.sem_perm.mode = (semflg & 0777) | SEM_ALLOC; sema[semid].cred = crhold(cred); sema[semid].u.sem_perm.seq = (sema[semid].u.sem_perm.seq + 1) & 0x7fff; sema[semid].u.sem_nsems = nsems; sema[semid].u.sem_otime = 0; sema[semid].u.sem_ctime = time_second; sema[semid].u.__sem_base = &sem[semtot]; semtot += nsems; bzero(sema[semid].u.__sem_base, sizeof(sema[semid].u.__sem_base[0])*nsems); #ifdef MAC mac_sysvsem_create(cred, &sema[semid]); #endif mtx_unlock(&sema_mtx[semid]); DPRINTF(("sembase = %p, next = %p\n", sema[semid].u.__sem_base, &sem[semtot])); } else { DPRINTF(("didn't find it and wasn't asked to create it\n")); error = ENOENT; goto done2; } found: td->td_retval[0] = IXSEQ_TO_IPCID(semid, sema[semid].u.sem_perm); done2: mtx_unlock(&sem_mtx); return (error); } #ifndef _SYS_SYSPROTO_H_ struct semop_args { int semid; struct sembuf *sops; size_t nsops; }; #endif int sys_semop(struct thread *td, struct semop_args *uap) { #define SMALL_SOPS 8 struct sembuf small_sops[SMALL_SOPS]; int semid = uap->semid; size_t nsops = uap->nsops; struct prison *rpr; struct sembuf *sops; struct semid_kernel *semakptr; struct sembuf *sopptr = NULL; struct sem *semptr = NULL; struct sem_undo *suptr; struct mtx *sema_mtxp; size_t i, j, k; int error; int do_wakeup, do_undos; unsigned short seq; #ifdef SEM_DEBUG sops = NULL; #endif DPRINTF(("call to semop(%d, %p, %u)\n", semid, sops, nsops)); AUDIT_ARG_SVIPC_ID(semid); rpr = sem_find_prison(td->td_ucred); if (sem == NULL) return (ENOSYS); semid = IPCID_TO_IX(semid); /* Convert back to zero origin */ if (semid < 0 || semid >= seminfo.semmni) return (EINVAL); /* Allocate memory for sem_ops */ if (nsops <= SMALL_SOPS) sops = small_sops; else if (nsops > seminfo.semopm) { DPRINTF(("too many sops (max=%d, nsops=%d)\n", seminfo.semopm, nsops)); return (E2BIG); } else { #ifdef RACCT if (racct_enable) { PROC_LOCK(td->td_proc); if (nsops > racct_get_available(td->td_proc, RACCT_NSEMOP)) { PROC_UNLOCK(td->td_proc); return (E2BIG); } PROC_UNLOCK(td->td_proc); } #endif sops = malloc(nsops * sizeof(*sops), M_TEMP, M_WAITOK); } if ((error = copyin(uap->sops, sops, nsops * sizeof(sops[0]))) != 0) { DPRINTF(("error = %d from copyin(%p, %p, %d)\n", error, uap->sops, sops, nsops * sizeof(sops[0]))); if (sops != small_sops) free(sops, M_TEMP); return (error); } semakptr = &sema[semid]; sema_mtxp = &sema_mtx[semid]; mtx_lock(sema_mtxp); if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0) { error = EINVAL; goto done2; } seq = semakptr->u.sem_perm.seq; if (seq != IPCID_TO_SEQ(uap->semid)) { error = EINVAL; goto done2; } if ((error = sem_prison_cansee(rpr, semakptr)) != 0) goto done2; /* * Initial pass through sops to see what permissions are needed. * Also perform any checks that don't need repeating on each * attempt to satisfy the request vector. */ j = 0; /* permission needed */ do_undos = 0; for (i = 0; i < nsops; i++) { sopptr = &sops[i]; if (sopptr->sem_num >= semakptr->u.sem_nsems) { error = EFBIG; goto done2; } if (sopptr->sem_flg & SEM_UNDO && sopptr->sem_op != 0) do_undos = 1; j |= (sopptr->sem_op == 0) ? SEM_R : SEM_A; } if ((error = ipcperm(td, &semakptr->u.sem_perm, j))) { DPRINTF(("error = %d from ipaccess\n", error)); goto done2; } #ifdef MAC error = mac_sysvsem_check_semop(td->td_ucred, semakptr, j); if (error != 0) goto done2; #endif /* * Loop trying to satisfy the vector of requests. * If we reach a point where we must wait, any requests already * performed are rolled back and we go to sleep until some other * process wakes us up. At this point, we start all over again. * * This ensures that from the perspective of other tasks, a set * of requests is atomic (never partially satisfied). */ for (;;) { do_wakeup = 0; error = 0; /* error return if necessary */ for (i = 0; i < nsops; i++) { sopptr = &sops[i]; semptr = &semakptr->u.__sem_base[sopptr->sem_num]; DPRINTF(( "semop: semakptr=%p, __sem_base=%p, " "semptr=%p, sem[%d]=%d : op=%d, flag=%s\n", semakptr, semakptr->u.__sem_base, semptr, sopptr->sem_num, semptr->semval, sopptr->sem_op, (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait")); if (sopptr->sem_op < 0) { if (semptr->semval + sopptr->sem_op < 0) { DPRINTF(("semop: can't do it now\n")); break; } else { semptr->semval += sopptr->sem_op; if (semptr->semval == 0 && semptr->semzcnt > 0) do_wakeup = 1; } } else if (sopptr->sem_op == 0) { if (semptr->semval != 0) { DPRINTF(("semop: not zero now\n")); break; } } else if (semptr->semval + sopptr->sem_op > seminfo.semvmx) { error = ERANGE; break; } else { if (semptr->semncnt > 0) do_wakeup = 1; semptr->semval += sopptr->sem_op; } } /* * Did we get through the entire vector? */ if (i >= nsops) goto done; /* * No ... rollback anything that we've already done */ DPRINTF(("semop: rollback 0 through %d\n", i-1)); for (j = 0; j < i; j++) semakptr->u.__sem_base[sops[j].sem_num].semval -= sops[j].sem_op; /* If we detected an error, return it */ if (error != 0) goto done2; /* * If the request that we couldn't satisfy has the * NOWAIT flag set then return with EAGAIN. */ if (sopptr->sem_flg & IPC_NOWAIT) { error = EAGAIN; goto done2; } if (sopptr->sem_op == 0) semptr->semzcnt++; else semptr->semncnt++; DPRINTF(("semop: good night!\n")); error = msleep(semakptr, sema_mtxp, (PZERO - 4) | PCATCH, "semwait", 0); DPRINTF(("semop: good morning (error=%d)!\n", error)); /* return code is checked below, after sem[nz]cnt-- */ /* * Make sure that the semaphore still exists */ seq = semakptr->u.sem_perm.seq; if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 || seq != IPCID_TO_SEQ(uap->semid)) { error = EIDRM; goto done2; } /* * Renew the semaphore's pointer after wakeup since * during msleep __sem_base may have been modified and semptr * is not valid any more */ semptr = &semakptr->u.__sem_base[sopptr->sem_num]; /* * The semaphore is still alive. Readjust the count of * waiting processes. */ if (sopptr->sem_op == 0) semptr->semzcnt--; else semptr->semncnt--; /* * Is it really morning, or was our sleep interrupted? * (Delayed check of msleep() return code because we * need to decrement sem[nz]cnt either way.) */ if (error != 0) { error = EINTR; goto done2; } DPRINTF(("semop: good morning!\n")); } done: /* * Process any SEM_UNDO requests. */ if (do_undos) { SEMUNDO_LOCK(); suptr = NULL; for (i = 0; i < nsops; i++) { /* * We only need to deal with SEM_UNDO's for non-zero * op's. */ int adjval; if ((sops[i].sem_flg & SEM_UNDO) == 0) continue; adjval = sops[i].sem_op; if (adjval == 0) continue; error = semundo_adjust(td, &suptr, semid, seq, sops[i].sem_num, -adjval); if (error == 0) continue; /* * Oh-Oh! We ran out of either sem_undo's or undo's. * Rollback the adjustments to this point and then * rollback the semaphore ups and down so we can return * with an error with all structures restored. We * rollback the undo's in the exact reverse order that * we applied them. This guarantees that we won't run * out of space as we roll things back out. */ for (j = 0; j < i; j++) { k = i - j - 1; if ((sops[k].sem_flg & SEM_UNDO) == 0) continue; adjval = sops[k].sem_op; if (adjval == 0) continue; if (semundo_adjust(td, &suptr, semid, seq, sops[k].sem_num, adjval) != 0) panic("semop - can't undo undos"); } for (j = 0; j < nsops; j++) semakptr->u.__sem_base[sops[j].sem_num].semval -= sops[j].sem_op; DPRINTF(("error = %d from semundo_adjust\n", error)); SEMUNDO_UNLOCK(); goto done2; } /* loop through the sops */ SEMUNDO_UNLOCK(); } /* if (do_undos) */ /* We're definitely done - set the sempid's and time */ for (i = 0; i < nsops; i++) { sopptr = &sops[i]; semptr = &semakptr->u.__sem_base[sopptr->sem_num]; semptr->sempid = td->td_proc->p_pid; } semakptr->u.sem_otime = time_second; /* * Do a wakeup if any semaphore was up'd whilst something was * sleeping on it. */ if (do_wakeup) { DPRINTF(("semop: doing wakeup\n")); wakeup(semakptr); DPRINTF(("semop: back from wakeup\n")); } DPRINTF(("semop: done\n")); td->td_retval[0] = 0; done2: mtx_unlock(sema_mtxp); if (sops != small_sops) free(sops, M_TEMP); return (error); } /* * Go through the undo structures for this process and apply the adjustments to * semaphores. */ static void semexit_myhook(void *arg, struct proc *p) { struct sem_undo *suptr; struct semid_kernel *semakptr; struct mtx *sema_mtxp; int semid, semnum, adjval, ix; unsigned short seq; /* * Go through the chain of undo vectors looking for one * associated with this process. */ if (LIST_EMPTY(&semu_list)) return; SEMUNDO_LOCK(); LIST_FOREACH(suptr, &semu_list, un_next) { if (suptr->un_proc == p) break; } if (suptr == NULL) { SEMUNDO_UNLOCK(); return; } LIST_REMOVE(suptr, un_next); DPRINTF(("proc @%p has undo structure with %d entries\n", p, suptr->un_cnt)); /* * If there are any active undo elements then process them. */ if (suptr->un_cnt > 0) { SEMUNDO_UNLOCK(); for (ix = 0; ix < suptr->un_cnt; ix++) { semid = suptr->un_ent[ix].un_id; semnum = suptr->un_ent[ix].un_num; adjval = suptr->un_ent[ix].un_adjval; seq = suptr->un_ent[ix].un_seq; semakptr = &sema[semid]; sema_mtxp = &sema_mtx[semid]; mtx_lock(sema_mtxp); if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 || (semakptr->u.sem_perm.seq != seq)) { mtx_unlock(sema_mtxp); continue; } if (semnum >= semakptr->u.sem_nsems) panic("semexit - semnum out of range"); DPRINTF(( "semexit: %p id=%d num=%d(adj=%d) ; sem=%d\n", suptr->un_proc, suptr->un_ent[ix].un_id, suptr->un_ent[ix].un_num, suptr->un_ent[ix].un_adjval, semakptr->u.__sem_base[semnum].semval)); if (adjval < 0 && semakptr->u.__sem_base[semnum].semval < -adjval) semakptr->u.__sem_base[semnum].semval = 0; else semakptr->u.__sem_base[semnum].semval += adjval; wakeup(semakptr); DPRINTF(("semexit: back from wakeup\n")); mtx_unlock(sema_mtxp); } SEMUNDO_LOCK(); } /* * Deallocate the undo vector. */ DPRINTF(("removing vector\n")); suptr->un_proc = NULL; suptr->un_cnt = 0; LIST_INSERT_HEAD(&semu_free_list, suptr, un_next); SEMUNDO_UNLOCK(); } static int sysctl_sema(SYSCTL_HANDLER_ARGS) { struct prison *pr, *rpr; struct semid_kernel tsemak; #ifdef COMPAT_FREEBSD32 struct semid_kernel32 tsemak32; #endif void *outaddr; size_t outsize; int error, i; pr = req->td->td_ucred->cr_prison; rpr = sem_find_prison(req->td->td_ucred); error = 0; for (i = 0; i < seminfo.semmni; i++) { mtx_lock(&sema_mtx[i]); if ((sema[i].u.sem_perm.mode & SEM_ALLOC) == 0 || rpr == NULL || sem_prison_cansee(rpr, &sema[i]) != 0) bzero(&tsemak, sizeof(tsemak)); else { tsemak = sema[i]; if (tsemak.cred->cr_prison != pr) tsemak.u.sem_perm.key = IPC_PRIVATE; } mtx_unlock(&sema_mtx[i]); #ifdef COMPAT_FREEBSD32 if (SV_CURPROC_FLAG(SV_ILP32)) { bzero(&tsemak32, sizeof(tsemak32)); freebsd32_ipcperm_out(&tsemak.u.sem_perm, &tsemak32.u.sem_perm); /* Don't copy u.__sem_base */ CP(tsemak, tsemak32, u.sem_nsems); CP(tsemak, tsemak32, u.sem_otime); CP(tsemak, tsemak32, u.sem_ctime); /* Don't copy label or cred */ outaddr = &tsemak32; outsize = sizeof(tsemak32); } else #endif { tsemak.u.__sem_base = NULL; tsemak.label = NULL; tsemak.cred = NULL; outaddr = &tsemak; outsize = sizeof(tsemak); } error = SYSCTL_OUT(req, outaddr, outsize); if (error != 0) break; } return (error); } static int sem_prison_check(void *obj, void *data) { struct prison *pr = obj; struct prison *prpr; struct vfsoptlist *opts = data; int error, jsys; /* * sysvsem is a jailsys integer. * It must be "disable" if the parent jail is disabled. */ error = vfs_copyopt(opts, "sysvsem", &jsys, sizeof(jsys)); if (error != ENOENT) { if (error != 0) return (error); switch (jsys) { case JAIL_SYS_DISABLE: break; case JAIL_SYS_NEW: case JAIL_SYS_INHERIT: prison_lock(pr->pr_parent); prpr = osd_jail_get(pr->pr_parent, sem_prison_slot); prison_unlock(pr->pr_parent); if (prpr == NULL) return (EPERM); break; default: return (EINVAL); } } return (0); } static int sem_prison_set(void *obj, void *data) { struct prison *pr = obj; struct prison *tpr, *orpr, *nrpr, *trpr; struct vfsoptlist *opts = data; void *rsv; int jsys, descend; /* * sysvsem controls which jail is the root of the associated sems (this * jail or same as the parent), or if the feature is available at all. */ if (vfs_copyopt(opts, "sysvsem", &jsys, sizeof(jsys)) == ENOENT) jsys = vfs_flagopt(opts, "allow.sysvipc", NULL, 0) ? JAIL_SYS_INHERIT : vfs_flagopt(opts, "allow.nosysvipc", NULL, 0) ? JAIL_SYS_DISABLE : -1; if (jsys == JAIL_SYS_DISABLE) { prison_lock(pr); orpr = osd_jail_get(pr, sem_prison_slot); if (orpr != NULL) osd_jail_del(pr, sem_prison_slot); prison_unlock(pr); if (orpr != NULL) { if (orpr == pr) sem_prison_cleanup(pr); /* Disable all child jails as well. */ FOREACH_PRISON_DESCENDANT(pr, tpr, descend) { prison_lock(tpr); trpr = osd_jail_get(tpr, sem_prison_slot); if (trpr != NULL) { osd_jail_del(tpr, sem_prison_slot); prison_unlock(tpr); if (trpr == tpr) sem_prison_cleanup(tpr); } else { prison_unlock(tpr); descend = 0; } } } } else if (jsys != -1) { if (jsys == JAIL_SYS_NEW) nrpr = pr; else { prison_lock(pr->pr_parent); nrpr = osd_jail_get(pr->pr_parent, sem_prison_slot); prison_unlock(pr->pr_parent); } rsv = osd_reserve(sem_prison_slot); prison_lock(pr); orpr = osd_jail_get(pr, sem_prison_slot); if (orpr != nrpr) (void)osd_jail_set_reserved(pr, sem_prison_slot, rsv, nrpr); else osd_free_reserved(rsv); prison_unlock(pr); if (orpr != nrpr) { if (orpr == pr) sem_prison_cleanup(pr); if (orpr != NULL) { /* Change child jails matching the old root, */ FOREACH_PRISON_DESCENDANT(pr, tpr, descend) { prison_lock(tpr); trpr = osd_jail_get(tpr, sem_prison_slot); if (trpr == orpr) { (void)osd_jail_set(tpr, sem_prison_slot, nrpr); prison_unlock(tpr); if (trpr == tpr) sem_prison_cleanup(tpr); } else { prison_unlock(tpr); descend = 0; } } } } } return (0); } static int sem_prison_get(void *obj, void *data) { struct prison *pr = obj; struct prison *rpr; struct vfsoptlist *opts = data; int error, jsys; /* Set sysvsem based on the jail's root prison. */ prison_lock(pr); rpr = osd_jail_get(pr, sem_prison_slot); prison_unlock(pr); jsys = rpr == NULL ? JAIL_SYS_DISABLE : rpr == pr ? JAIL_SYS_NEW : JAIL_SYS_INHERIT; error = vfs_setopt(opts, "sysvsem", &jsys, sizeof(jsys)); if (error == ENOENT) error = 0; return (error); } static int sem_prison_remove(void *obj, void *data __unused) { struct prison *pr = obj; struct prison *rpr; prison_lock(pr); rpr = osd_jail_get(pr, sem_prison_slot); prison_unlock(pr); if (rpr == pr) sem_prison_cleanup(pr); return (0); } static void sem_prison_cleanup(struct prison *pr) { int i; /* Remove any sems that belong to this jail. */ mtx_lock(&sem_mtx); for (i = 0; i < seminfo.semmni; i++) { if ((sema[i].u.sem_perm.mode & SEM_ALLOC) && sema[i].cred != NULL && sema[i].cred->cr_prison == pr) { mtx_lock(&sema_mtx[i]); sem_remove(i, NULL); mtx_unlock(&sema_mtx[i]); } } mtx_unlock(&sem_mtx); } SYSCTL_JAIL_PARAM_SYS_NODE(sysvsem, CTLFLAG_RW, "SYSV semaphores"); #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) /* XXX casting to (sy_call_t *) is bogus, as usual. */ static sy_call_t *semcalls[] = { (sy_call_t *)freebsd7___semctl, (sy_call_t *)sys_semget, (sy_call_t *)sys_semop }; /* * Entry point for all SEM calls. */ int sys_semsys(td, uap) struct thread *td; /* XXX actually varargs. */ struct semsys_args /* { int which; int a2; int a3; int a4; int a5; } */ *uap; { int error; AUDIT_ARG_SVIPC_WHICH(uap->which); if (uap->which < 0 || uap->which >= nitems(semcalls)) return (EINVAL); error = (*semcalls[uap->which])(td, &uap->a2); return (error); } - -#ifndef CP -#define CP(src, dst, fld) do { (dst).fld = (src).fld; } while (0) -#endif #ifndef _SYS_SYSPROTO_H_ struct freebsd7___semctl_args { int semid; int semnum; int cmd; union semun_old *arg; }; #endif int freebsd7___semctl(struct thread *td, struct freebsd7___semctl_args *uap) { struct semid_ds_old dsold; struct semid_ds dsbuf; union semun_old arg; union semun semun; register_t rval; int error; switch (uap->cmd) { case SEM_STAT: case IPC_SET: case IPC_STAT: case GETALL: case SETVAL: case SETALL: error = copyin(uap->arg, &arg, sizeof(arg)); if (error) return (error); break; } switch (uap->cmd) { case SEM_STAT: case IPC_STAT: semun.buf = &dsbuf; break; case IPC_SET: error = copyin(arg.buf, &dsold, sizeof(dsold)); if (error) return (error); ipcperm_old2new(&dsold.sem_perm, &dsbuf.sem_perm); CP(dsold, dsbuf, __sem_base); CP(dsold, dsbuf, sem_nsems); CP(dsold, dsbuf, sem_otime); CP(dsold, dsbuf, sem_ctime); semun.buf = &dsbuf; break; case GETALL: case SETALL: semun.array = arg.array; break; case SETVAL: semun.val = arg.val; break; } error = kern_semctl(td, uap->semid, uap->semnum, uap->cmd, &semun, &rval); if (error) return (error); switch (uap->cmd) { case SEM_STAT: case IPC_STAT: bzero(&dsold, sizeof(dsold)); ipcperm_new2old(&dsbuf.sem_perm, &dsold.sem_perm); CP(dsbuf, dsold, __sem_base); CP(dsbuf, dsold, sem_nsems); CP(dsbuf, dsold, sem_otime); CP(dsbuf, dsold, sem_ctime); error = copyout(&dsold, arg.buf, sizeof(dsold)); break; } if (error == 0) td->td_retval[0] = rval; return (error); } #endif /* COMPAT_FREEBSD{4,5,6,7} */ #ifdef COMPAT_FREEBSD32 int freebsd32_semsys(struct thread *td, struct freebsd32_semsys_args *uap) { #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) AUDIT_ARG_SVIPC_WHICH(uap->which); switch (uap->which) { case 0: return (freebsd7_freebsd32_semctl(td, (struct freebsd7_freebsd32_semctl_args *)&uap->a2)); default: return (sys_semsys(td, (struct semsys_args *)uap)); } #else return (nosys(td, NULL)); #endif } #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) int freebsd7_freebsd32_semctl(struct thread *td, struct freebsd7_freebsd32_semctl_args *uap) { struct semid_ds32_old dsbuf32; struct semid_ds dsbuf; union semun semun; union semun32 arg; register_t rval; int error; switch (uap->cmd) { case SEM_STAT: case IPC_SET: case IPC_STAT: case GETALL: case SETVAL: case SETALL: error = copyin(uap->arg, &arg, sizeof(arg)); if (error) return (error); break; } switch (uap->cmd) { case SEM_STAT: case IPC_STAT: semun.buf = &dsbuf; break; case IPC_SET: error = copyin(PTRIN(arg.buf), &dsbuf32, sizeof(dsbuf32)); if (error) return (error); freebsd32_ipcperm_old_in(&dsbuf32.sem_perm, &dsbuf.sem_perm); PTRIN_CP(dsbuf32, dsbuf, __sem_base); CP(dsbuf32, dsbuf, sem_nsems); CP(dsbuf32, dsbuf, sem_otime); CP(dsbuf32, dsbuf, sem_ctime); semun.buf = &dsbuf; break; case GETALL: case SETALL: semun.array = PTRIN(arg.array); break; case SETVAL: semun.val = arg.val; break; } error = kern_semctl(td, uap->semid, uap->semnum, uap->cmd, &semun, &rval); if (error) return (error); switch (uap->cmd) { case SEM_STAT: case IPC_STAT: bzero(&dsbuf32, sizeof(dsbuf32)); freebsd32_ipcperm_old_out(&dsbuf.sem_perm, &dsbuf32.sem_perm); PTROUT_CP(dsbuf, dsbuf32, __sem_base); CP(dsbuf, dsbuf32, sem_nsems); CP(dsbuf, dsbuf32, sem_otime); CP(dsbuf, dsbuf32, sem_ctime); error = copyout(&dsbuf32, PTRIN(arg.buf), sizeof(dsbuf32)); break; } if (error == 0) td->td_retval[0] = rval; return (error); } #endif int freebsd32_semctl(struct thread *td, struct freebsd32_semctl_args *uap) { struct semid_ds32 dsbuf32; struct semid_ds dsbuf; union semun semun; union semun32 arg; register_t rval; int error; switch (uap->cmd) { case SEM_STAT: case IPC_SET: case IPC_STAT: case GETALL: case SETVAL: case SETALL: error = copyin(uap->arg, &arg, sizeof(arg)); if (error) return (error); break; } switch (uap->cmd) { case SEM_STAT: case IPC_STAT: semun.buf = &dsbuf; break; case IPC_SET: error = copyin(PTRIN(arg.buf), &dsbuf32, sizeof(dsbuf32)); if (error) return (error); freebsd32_ipcperm_in(&dsbuf32.sem_perm, &dsbuf.sem_perm); PTRIN_CP(dsbuf32, dsbuf, __sem_base); CP(dsbuf32, dsbuf, sem_nsems); CP(dsbuf32, dsbuf, sem_otime); CP(dsbuf32, dsbuf, sem_ctime); semun.buf = &dsbuf; break; case GETALL: case SETALL: semun.array = PTRIN(arg.array); break; case SETVAL: semun.val = arg.val; break; } error = kern_semctl(td, uap->semid, uap->semnum, uap->cmd, &semun, &rval); if (error) return (error); switch (uap->cmd) { case SEM_STAT: case IPC_STAT: bzero(&dsbuf32, sizeof(dsbuf32)); freebsd32_ipcperm_out(&dsbuf.sem_perm, &dsbuf32.sem_perm); PTROUT_CP(dsbuf, dsbuf32, __sem_base); CP(dsbuf, dsbuf32, sem_nsems); CP(dsbuf, dsbuf32, sem_otime); CP(dsbuf, dsbuf32, sem_ctime); error = copyout(&dsbuf32, PTRIN(arg.buf), sizeof(dsbuf32)); break; } if (error == 0) td->td_retval[0] = rval; return (error); } #endif /* COMPAT_FREEBSD32 */ Index: stable/12/sys/kern/sysv_shm.c =================================================================== --- stable/12/sys/kern/sysv_shm.c (revision 360445) +++ stable/12/sys/kern/sysv_shm.c (revision 360446) @@ -1,1697 +1,1694 @@ /*- * SPDX-License-Identifier: BSD-4-Clause AND BSD-2-Clause-FreeBSD * * Copyright (c) 1994 Adam Glass and Charles Hannum. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Adam Glass and Charles * Hannum. * 4. The names of the authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $NetBSD: sysv_shm.c,v 1.39 1997/10/07 10:02:03 drochner Exp $ */ /*- * Copyright (c) 2003-2005 McAfee, Inc. * Copyright (c) 2016-2017 Robert N. M. Watson * All rights reserved. * * This software was developed for the FreeBSD Project in part by McAfee * Research, the Security Research Division of McAfee, Inc under DARPA/SPAWAR * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS research * program. * * Portions of this software were developed by BAE Systems, the University of * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent * Computing (TC) research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_sysvipc.h" #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include FEATURE(sysv_shm, "System V shared memory segments support"); static MALLOC_DEFINE(M_SHM, "shm", "SVID compatible shared memory segments"); static int shmget_allocate_segment(struct thread *td, struct shmget_args *uap, int mode); static int shmget_existing(struct thread *td, struct shmget_args *uap, int mode, int segnum); #define SHMSEG_FREE 0x0200 #define SHMSEG_REMOVED 0x0400 #define SHMSEG_ALLOCATED 0x0800 static int shm_last_free, shm_nused, shmalloced; vm_size_t shm_committed; static struct shmid_kernel *shmsegs; static unsigned shm_prison_slot; struct shmmap_state { vm_offset_t va; int shmid; }; static void shm_deallocate_segment(struct shmid_kernel *); static int shm_find_segment_by_key(struct prison *, key_t); static struct shmid_kernel *shm_find_segment(struct prison *, int, bool); static int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *); static void shmrealloc(void); static int shminit(void); static int sysvshm_modload(struct module *, int, void *); static int shmunload(void); static void shmexit_myhook(struct vmspace *vm); static void shmfork_myhook(struct proc *p1, struct proc *p2); static int sysctl_shmsegs(SYSCTL_HANDLER_ARGS); static void shm_remove(struct shmid_kernel *, int); static struct prison *shm_find_prison(struct ucred *); static int shm_prison_cansee(struct prison *, struct shmid_kernel *); static int shm_prison_check(void *, void *); static int shm_prison_set(void *, void *); static int shm_prison_get(void *, void *); static int shm_prison_remove(void *, void *); static void shm_prison_cleanup(struct prison *); /* * Tuneable values. */ #ifndef SHMMAXPGS #define SHMMAXPGS 131072 /* Note: sysv shared memory is swap backed. */ #endif #ifndef SHMMAX #define SHMMAX (SHMMAXPGS*PAGE_SIZE) #endif #ifndef SHMMIN #define SHMMIN 1 #endif #ifndef SHMMNI #define SHMMNI 192 #endif #ifndef SHMSEG #define SHMSEG 128 #endif #ifndef SHMALL #define SHMALL (SHMMAXPGS) #endif struct shminfo shminfo = { .shmmax = SHMMAX, .shmmin = SHMMIN, .shmmni = SHMMNI, .shmseg = SHMSEG, .shmall = SHMALL }; static int shm_use_phys; static int shm_allow_removed = 1; SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmax, CTLFLAG_RWTUN, &shminfo.shmmax, 0, "Maximum shared memory segment size"); SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmin, CTLFLAG_RWTUN, &shminfo.shmmin, 0, "Minimum shared memory segment size"); SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmmni, CTLFLAG_RDTUN, &shminfo.shmmni, 0, "Number of shared memory identifiers"); SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmseg, CTLFLAG_RDTUN, &shminfo.shmseg, 0, "Number of segments per process"); SYSCTL_ULONG(_kern_ipc, OID_AUTO, shmall, CTLFLAG_RWTUN, &shminfo.shmall, 0, "Maximum number of pages available for shared memory"); SYSCTL_INT(_kern_ipc, OID_AUTO, shm_use_phys, CTLFLAG_RWTUN, &shm_use_phys, 0, "Enable/Disable locking of shared memory pages in core"); SYSCTL_INT(_kern_ipc, OID_AUTO, shm_allow_removed, CTLFLAG_RWTUN, &shm_allow_removed, 0, "Enable/Disable attachment to attached segments marked for removal"); SYSCTL_PROC(_kern_ipc, OID_AUTO, shmsegs, CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, sysctl_shmsegs, "", "Array of struct shmid_kernel for each potential shared memory segment"); static struct sx sysvshmsx; #define SYSVSHM_LOCK() sx_xlock(&sysvshmsx) #define SYSVSHM_UNLOCK() sx_xunlock(&sysvshmsx) #define SYSVSHM_ASSERT_LOCKED() sx_assert(&sysvshmsx, SA_XLOCKED) static int shm_find_segment_by_key(struct prison *pr, key_t key) { int i; for (i = 0; i < shmalloced; i++) if ((shmsegs[i].u.shm_perm.mode & SHMSEG_ALLOCATED) && shmsegs[i].cred != NULL && shmsegs[i].cred->cr_prison == pr && shmsegs[i].u.shm_perm.key == key) return (i); return (-1); } /* * Finds segment either by shmid if is_shmid is true, or by segnum if * is_shmid is false. */ static struct shmid_kernel * shm_find_segment(struct prison *rpr, int arg, bool is_shmid) { struct shmid_kernel *shmseg; int segnum; segnum = is_shmid ? IPCID_TO_IX(arg) : arg; if (segnum < 0 || segnum >= shmalloced) return (NULL); shmseg = &shmsegs[segnum]; if ((shmseg->u.shm_perm.mode & SHMSEG_ALLOCATED) == 0 || (!shm_allow_removed && (shmseg->u.shm_perm.mode & SHMSEG_REMOVED) != 0) || (is_shmid && shmseg->u.shm_perm.seq != IPCID_TO_SEQ(arg)) || shm_prison_cansee(rpr, shmseg) != 0) return (NULL); return (shmseg); } static void shm_deallocate_segment(struct shmid_kernel *shmseg) { vm_size_t size; SYSVSHM_ASSERT_LOCKED(); vm_object_deallocate(shmseg->object); shmseg->object = NULL; size = round_page(shmseg->u.shm_segsz); shm_committed -= btoc(size); shm_nused--; shmseg->u.shm_perm.mode = SHMSEG_FREE; #ifdef MAC mac_sysvshm_cleanup(shmseg); #endif racct_sub_cred(shmseg->cred, RACCT_NSHM, 1); racct_sub_cred(shmseg->cred, RACCT_SHMSIZE, size); crfree(shmseg->cred); shmseg->cred = NULL; } static int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s) { struct shmid_kernel *shmseg; int segnum, result; vm_size_t size; SYSVSHM_ASSERT_LOCKED(); segnum = IPCID_TO_IX(shmmap_s->shmid); KASSERT(segnum >= 0 && segnum < shmalloced, ("segnum %d shmalloced %d", segnum, shmalloced)); shmseg = &shmsegs[segnum]; size = round_page(shmseg->u.shm_segsz); result = vm_map_remove(&vm->vm_map, shmmap_s->va, shmmap_s->va + size); if (result != KERN_SUCCESS) return (EINVAL); shmmap_s->shmid = -1; shmseg->u.shm_dtime = time_second; if (--shmseg->u.shm_nattch == 0 && (shmseg->u.shm_perm.mode & SHMSEG_REMOVED)) { shm_deallocate_segment(shmseg); shm_last_free = segnum; } return (0); } static void shm_remove(struct shmid_kernel *shmseg, int segnum) { shmseg->u.shm_perm.key = IPC_PRIVATE; shmseg->u.shm_perm.mode |= SHMSEG_REMOVED; if (shmseg->u.shm_nattch == 0) { shm_deallocate_segment(shmseg); shm_last_free = segnum; } } static struct prison * shm_find_prison(struct ucred *cred) { struct prison *pr, *rpr; pr = cred->cr_prison; prison_lock(pr); rpr = osd_jail_get(pr, shm_prison_slot); prison_unlock(pr); return rpr; } static int shm_prison_cansee(struct prison *rpr, struct shmid_kernel *shmseg) { if (shmseg->cred == NULL || !(rpr == shmseg->cred->cr_prison || prison_ischild(rpr, shmseg->cred->cr_prison))) return (EINVAL); return (0); } static int kern_shmdt_locked(struct thread *td, const void *shmaddr) { struct proc *p = td->td_proc; struct shmmap_state *shmmap_s; #ifdef MAC int error; #endif int i; SYSVSHM_ASSERT_LOCKED(); if (shm_find_prison(td->td_ucred) == NULL) return (ENOSYS); shmmap_s = p->p_vmspace->vm_shm; if (shmmap_s == NULL) return (EINVAL); AUDIT_ARG_SVIPC_ID(shmmap_s->shmid); for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) { if (shmmap_s->shmid != -1 && shmmap_s->va == (vm_offset_t)shmaddr) { break; } } if (i == shminfo.shmseg) return (EINVAL); #ifdef MAC error = mac_sysvshm_check_shmdt(td->td_ucred, &shmsegs[IPCID_TO_IX(shmmap_s->shmid)]); if (error != 0) return (error); #endif return (shm_delete_mapping(p->p_vmspace, shmmap_s)); } #ifndef _SYS_SYSPROTO_H_ struct shmdt_args { const void *shmaddr; }; #endif int sys_shmdt(struct thread *td, struct shmdt_args *uap) { int error; SYSVSHM_LOCK(); error = kern_shmdt_locked(td, uap->shmaddr); SYSVSHM_UNLOCK(); return (error); } static int kern_shmat_locked(struct thread *td, int shmid, const void *shmaddr, int shmflg) { struct prison *rpr; struct proc *p = td->td_proc; struct shmid_kernel *shmseg; struct shmmap_state *shmmap_s; vm_offset_t attach_va; vm_prot_t prot; vm_size_t size; int cow, error, find_space, i, rv; AUDIT_ARG_SVIPC_ID(shmid); AUDIT_ARG_VALUE(shmflg); SYSVSHM_ASSERT_LOCKED(); rpr = shm_find_prison(td->td_ucred); if (rpr == NULL) return (ENOSYS); shmmap_s = p->p_vmspace->vm_shm; if (shmmap_s == NULL) { shmmap_s = malloc(shminfo.shmseg * sizeof(struct shmmap_state), M_SHM, M_WAITOK); for (i = 0; i < shminfo.shmseg; i++) shmmap_s[i].shmid = -1; KASSERT(p->p_vmspace->vm_shm == NULL, ("raced")); p->p_vmspace->vm_shm = shmmap_s; } shmseg = shm_find_segment(rpr, shmid, true); if (shmseg == NULL) return (EINVAL); error = ipcperm(td, &shmseg->u.shm_perm, (shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); if (error != 0) return (error); #ifdef MAC error = mac_sysvshm_check_shmat(td->td_ucred, shmseg, shmflg); if (error != 0) return (error); #endif for (i = 0; i < shminfo.shmseg; i++) { if (shmmap_s->shmid == -1) break; shmmap_s++; } if (i >= shminfo.shmseg) return (EMFILE); size = round_page(shmseg->u.shm_segsz); prot = VM_PROT_READ; cow = MAP_INHERIT_SHARE | MAP_PREFAULT_PARTIAL; if ((shmflg & SHM_RDONLY) == 0) prot |= VM_PROT_WRITE; if (shmaddr != NULL) { if ((shmflg & SHM_RND) != 0) attach_va = rounddown2((vm_offset_t)shmaddr, SHMLBA); else if (((vm_offset_t)shmaddr & (SHMLBA-1)) == 0) attach_va = (vm_offset_t)shmaddr; else return (EINVAL); if ((shmflg & SHM_REMAP) != 0) cow |= MAP_REMAP; find_space = VMFS_NO_SPACE; } else { /* * This is just a hint to vm_map_find() about where to * put it. */ attach_va = round_page((vm_offset_t)p->p_vmspace->vm_daddr + lim_max(td, RLIMIT_DATA)); find_space = VMFS_OPTIMAL_SPACE; } vm_object_reference(shmseg->object); rv = vm_map_find(&p->p_vmspace->vm_map, shmseg->object, 0, &attach_va, size, 0, find_space, prot, prot, cow); if (rv != KERN_SUCCESS) { vm_object_deallocate(shmseg->object); return (ENOMEM); } shmmap_s->va = attach_va; shmmap_s->shmid = shmid; shmseg->u.shm_lpid = p->p_pid; shmseg->u.shm_atime = time_second; shmseg->u.shm_nattch++; td->td_retval[0] = attach_va; return (error); } int kern_shmat(struct thread *td, int shmid, const void *shmaddr, int shmflg) { int error; SYSVSHM_LOCK(); error = kern_shmat_locked(td, shmid, shmaddr, shmflg); SYSVSHM_UNLOCK(); return (error); } #ifndef _SYS_SYSPROTO_H_ struct shmat_args { int shmid; const void *shmaddr; int shmflg; }; #endif int sys_shmat(struct thread *td, struct shmat_args *uap) { return (kern_shmat(td, uap->shmid, uap->shmaddr, uap->shmflg)); } static int kern_shmctl_locked(struct thread *td, int shmid, int cmd, void *buf, size_t *bufsz) { struct prison *rpr; struct shmid_kernel *shmseg; struct shmid_ds *shmidp; struct shm_info shm_info; int error; SYSVSHM_ASSERT_LOCKED(); rpr = shm_find_prison(td->td_ucred); if (rpr == NULL) return (ENOSYS); AUDIT_ARG_SVIPC_ID(shmid); AUDIT_ARG_SVIPC_CMD(cmd); switch (cmd) { /* * It is possible that kern_shmctl is being called from the Linux ABI * layer, in which case, we will need to implement IPC_INFO. It should * be noted that other shmctl calls will be funneled through here for * Linix binaries as well. * * NB: The Linux ABI layer will convert this data to structure(s) more * consistent with the Linux ABI. */ case IPC_INFO: memcpy(buf, &shminfo, sizeof(shminfo)); if (bufsz) *bufsz = sizeof(shminfo); td->td_retval[0] = shmalloced; return (0); case SHM_INFO: { shm_info.used_ids = shm_nused; shm_info.shm_rss = 0; /*XXX where to get from ? */ shm_info.shm_tot = 0; /*XXX where to get from ? */ shm_info.shm_swp = 0; /*XXX where to get from ? */ shm_info.swap_attempts = 0; /*XXX where to get from ? */ shm_info.swap_successes = 0; /*XXX where to get from ? */ memcpy(buf, &shm_info, sizeof(shm_info)); if (bufsz != NULL) *bufsz = sizeof(shm_info); td->td_retval[0] = shmalloced; return (0); } } shmseg = shm_find_segment(rpr, shmid, cmd != SHM_STAT); if (shmseg == NULL) return (EINVAL); #ifdef MAC error = mac_sysvshm_check_shmctl(td->td_ucred, shmseg, cmd); if (error != 0) return (error); #endif switch (cmd) { case SHM_STAT: case IPC_STAT: shmidp = (struct shmid_ds *)buf; error = ipcperm(td, &shmseg->u.shm_perm, IPC_R); if (error != 0) return (error); memcpy(shmidp, &shmseg->u, sizeof(struct shmid_ds)); if (td->td_ucred->cr_prison != shmseg->cred->cr_prison) shmidp->shm_perm.key = IPC_PRIVATE; if (bufsz != NULL) *bufsz = sizeof(struct shmid_ds); if (cmd == SHM_STAT) { td->td_retval[0] = IXSEQ_TO_IPCID(shmid, shmseg->u.shm_perm); } break; case IPC_SET: shmidp = (struct shmid_ds *)buf; AUDIT_ARG_SVIPC_PERM(&shmidp->shm_perm); error = ipcperm(td, &shmseg->u.shm_perm, IPC_M); if (error != 0) return (error); shmseg->u.shm_perm.uid = shmidp->shm_perm.uid; shmseg->u.shm_perm.gid = shmidp->shm_perm.gid; shmseg->u.shm_perm.mode = (shmseg->u.shm_perm.mode & ~ACCESSPERMS) | (shmidp->shm_perm.mode & ACCESSPERMS); shmseg->u.shm_ctime = time_second; break; case IPC_RMID: error = ipcperm(td, &shmseg->u.shm_perm, IPC_M); if (error != 0) return (error); shm_remove(shmseg, IPCID_TO_IX(shmid)); break; #if 0 case SHM_LOCK: case SHM_UNLOCK: #endif default: error = EINVAL; break; } return (error); } int kern_shmctl(struct thread *td, int shmid, int cmd, void *buf, size_t *bufsz) { int error; SYSVSHM_LOCK(); error = kern_shmctl_locked(td, shmid, cmd, buf, bufsz); SYSVSHM_UNLOCK(); return (error); } #ifndef _SYS_SYSPROTO_H_ struct shmctl_args { int shmid; int cmd; struct shmid_ds *buf; }; #endif int sys_shmctl(struct thread *td, struct shmctl_args *uap) { int error; struct shmid_ds buf; size_t bufsz; /* * The only reason IPC_INFO, SHM_INFO, SHM_STAT exists is to support * Linux binaries. If we see the call come through the FreeBSD ABI, * return an error back to the user since we do not to support this. */ if (uap->cmd == IPC_INFO || uap->cmd == SHM_INFO || uap->cmd == SHM_STAT) return (EINVAL); /* IPC_SET needs to copyin the buffer before calling kern_shmctl */ if (uap->cmd == IPC_SET) { if ((error = copyin(uap->buf, &buf, sizeof(struct shmid_ds)))) goto done; } error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz); if (error) goto done; /* Cases in which we need to copyout */ switch (uap->cmd) { case IPC_STAT: error = copyout(&buf, uap->buf, bufsz); break; } done: if (error) { /* Invalidate the return value */ td->td_retval[0] = -1; } return (error); } static int shmget_existing(struct thread *td, struct shmget_args *uap, int mode, int segnum) { struct shmid_kernel *shmseg; #ifdef MAC int error; #endif SYSVSHM_ASSERT_LOCKED(); KASSERT(segnum >= 0 && segnum < shmalloced, ("segnum %d shmalloced %d", segnum, shmalloced)); shmseg = &shmsegs[segnum]; if ((uap->shmflg & (IPC_CREAT | IPC_EXCL)) == (IPC_CREAT | IPC_EXCL)) return (EEXIST); #ifdef MAC error = mac_sysvshm_check_shmget(td->td_ucred, shmseg, uap->shmflg); if (error != 0) return (error); #endif if (uap->size != 0 && uap->size > shmseg->u.shm_segsz) return (EINVAL); td->td_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->u.shm_perm); return (0); } static int shmget_allocate_segment(struct thread *td, struct shmget_args *uap, int mode) { struct ucred *cred = td->td_ucred; struct shmid_kernel *shmseg; vm_object_t shm_object; int i, segnum; size_t size; SYSVSHM_ASSERT_LOCKED(); if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) return (EINVAL); if (shm_nused >= shminfo.shmmni) /* Any shmids left? */ return (ENOSPC); size = round_page(uap->size); if (shm_committed + btoc(size) > shminfo.shmall) return (ENOMEM); if (shm_last_free < 0) { shmrealloc(); /* Maybe expand the shmsegs[] array. */ for (i = 0; i < shmalloced; i++) if (shmsegs[i].u.shm_perm.mode & SHMSEG_FREE) break; if (i == shmalloced) return (ENOSPC); segnum = i; } else { segnum = shm_last_free; shm_last_free = -1; } KASSERT(segnum >= 0 && segnum < shmalloced, ("segnum %d shmalloced %d", segnum, shmalloced)); shmseg = &shmsegs[segnum]; #ifdef RACCT if (racct_enable) { PROC_LOCK(td->td_proc); if (racct_add(td->td_proc, RACCT_NSHM, 1)) { PROC_UNLOCK(td->td_proc); return (ENOSPC); } if (racct_add(td->td_proc, RACCT_SHMSIZE, size)) { racct_sub(td->td_proc, RACCT_NSHM, 1); PROC_UNLOCK(td->td_proc); return (ENOMEM); } PROC_UNLOCK(td->td_proc); } #endif /* * We make sure that we have allocated a pager before we need * to. */ shm_object = vm_pager_allocate(shm_use_phys ? OBJT_PHYS : OBJT_SWAP, 0, size, VM_PROT_DEFAULT, 0, cred); if (shm_object == NULL) { #ifdef RACCT if (racct_enable) { PROC_LOCK(td->td_proc); racct_sub(td->td_proc, RACCT_NSHM, 1); racct_sub(td->td_proc, RACCT_SHMSIZE, size); PROC_UNLOCK(td->td_proc); } #endif return (ENOMEM); } shm_object->pg_color = 0; VM_OBJECT_WLOCK(shm_object); vm_object_clear_flag(shm_object, OBJ_ONEMAPPING); vm_object_set_flag(shm_object, OBJ_COLORED | OBJ_NOSPLIT); VM_OBJECT_WUNLOCK(shm_object); shmseg->object = shm_object; shmseg->u.shm_perm.cuid = shmseg->u.shm_perm.uid = cred->cr_uid; shmseg->u.shm_perm.cgid = shmseg->u.shm_perm.gid = cred->cr_gid; shmseg->u.shm_perm.mode = (mode & ACCESSPERMS) | SHMSEG_ALLOCATED; shmseg->u.shm_perm.key = uap->key; shmseg->u.shm_perm.seq = (shmseg->u.shm_perm.seq + 1) & 0x7fff; shmseg->cred = crhold(cred); shmseg->u.shm_segsz = uap->size; shmseg->u.shm_cpid = td->td_proc->p_pid; shmseg->u.shm_lpid = shmseg->u.shm_nattch = 0; shmseg->u.shm_atime = shmseg->u.shm_dtime = 0; #ifdef MAC mac_sysvshm_create(cred, shmseg); #endif shmseg->u.shm_ctime = time_second; shm_committed += btoc(size); shm_nused++; td->td_retval[0] = IXSEQ_TO_IPCID(segnum, shmseg->u.shm_perm); return (0); } #ifndef _SYS_SYSPROTO_H_ struct shmget_args { key_t key; size_t size; int shmflg; }; #endif int sys_shmget(struct thread *td, struct shmget_args *uap) { int segnum, mode; int error; if (shm_find_prison(td->td_ucred) == NULL) return (ENOSYS); mode = uap->shmflg & ACCESSPERMS; SYSVSHM_LOCK(); if (uap->key == IPC_PRIVATE) { error = shmget_allocate_segment(td, uap, mode); } else { segnum = shm_find_segment_by_key(td->td_ucred->cr_prison, uap->key); if (segnum >= 0) error = shmget_existing(td, uap, mode, segnum); else if ((uap->shmflg & IPC_CREAT) == 0) error = ENOENT; else error = shmget_allocate_segment(td, uap, mode); } SYSVSHM_UNLOCK(); return (error); } static void shmfork_myhook(struct proc *p1, struct proc *p2) { struct shmmap_state *shmmap_s; size_t size; int i; SYSVSHM_LOCK(); size = shminfo.shmseg * sizeof(struct shmmap_state); shmmap_s = malloc(size, M_SHM, M_WAITOK); bcopy(p1->p_vmspace->vm_shm, shmmap_s, size); p2->p_vmspace->vm_shm = shmmap_s; for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) { if (shmmap_s->shmid != -1) { KASSERT(IPCID_TO_IX(shmmap_s->shmid) >= 0 && IPCID_TO_IX(shmmap_s->shmid) < shmalloced, ("segnum %d shmalloced %d", IPCID_TO_IX(shmmap_s->shmid), shmalloced)); shmsegs[IPCID_TO_IX(shmmap_s->shmid)].u.shm_nattch++; } } SYSVSHM_UNLOCK(); } static void shmexit_myhook(struct vmspace *vm) { struct shmmap_state *base, *shm; int i; base = vm->vm_shm; if (base != NULL) { vm->vm_shm = NULL; SYSVSHM_LOCK(); for (i = 0, shm = base; i < shminfo.shmseg; i++, shm++) { if (shm->shmid != -1) shm_delete_mapping(vm, shm); } SYSVSHM_UNLOCK(); free(base, M_SHM); } } static void shmrealloc(void) { struct shmid_kernel *newsegs; int i; SYSVSHM_ASSERT_LOCKED(); if (shmalloced >= shminfo.shmmni) return; newsegs = malloc(shminfo.shmmni * sizeof(*newsegs), M_SHM, M_WAITOK | M_ZERO); for (i = 0; i < shmalloced; i++) bcopy(&shmsegs[i], &newsegs[i], sizeof(newsegs[0])); for (; i < shminfo.shmmni; i++) { newsegs[i].u.shm_perm.mode = SHMSEG_FREE; newsegs[i].u.shm_perm.seq = 0; #ifdef MAC mac_sysvshm_init(&newsegs[i]); #endif } free(shmsegs, M_SHM); shmsegs = newsegs; shmalloced = shminfo.shmmni; } static struct syscall_helper_data shm_syscalls[] = { SYSCALL_INIT_HELPER(shmat), SYSCALL_INIT_HELPER(shmctl), SYSCALL_INIT_HELPER(shmdt), SYSCALL_INIT_HELPER(shmget), #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) SYSCALL_INIT_HELPER_COMPAT(freebsd7_shmctl), #endif #if defined(__i386__) && (defined(COMPAT_FREEBSD4) || defined(COMPAT_43)) SYSCALL_INIT_HELPER(shmsys), #endif SYSCALL_INIT_LAST }; #ifdef COMPAT_FREEBSD32 #include #include #include #include #include #include static struct syscall_helper_data shm32_syscalls[] = { SYSCALL32_INIT_HELPER_COMPAT(shmat), SYSCALL32_INIT_HELPER_COMPAT(shmdt), SYSCALL32_INIT_HELPER_COMPAT(shmget), SYSCALL32_INIT_HELPER(freebsd32_shmsys), SYSCALL32_INIT_HELPER(freebsd32_shmctl), #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) SYSCALL32_INIT_HELPER(freebsd7_freebsd32_shmctl), #endif SYSCALL_INIT_LAST }; #endif static int shminit(void) { struct prison *pr; void **rsv; int i, error; osd_method_t methods[PR_MAXMETHOD] = { [PR_METHOD_CHECK] = shm_prison_check, [PR_METHOD_SET] = shm_prison_set, [PR_METHOD_GET] = shm_prison_get, [PR_METHOD_REMOVE] = shm_prison_remove, }; #ifndef BURN_BRIDGES if (TUNABLE_ULONG_FETCH("kern.ipc.shmmaxpgs", &shminfo.shmall) != 0) printf("kern.ipc.shmmaxpgs is now called kern.ipc.shmall!\n"); #endif if (shminfo.shmmax == SHMMAX) { /* Initialize shmmax dealing with possible overflow. */ for (i = PAGE_SIZE; i != 0; i--) { shminfo.shmmax = shminfo.shmall * i; if ((shminfo.shmmax / shminfo.shmall) == (u_long)i) break; } } shmalloced = shminfo.shmmni; shmsegs = malloc(shmalloced * sizeof(shmsegs[0]), M_SHM, M_WAITOK|M_ZERO); for (i = 0; i < shmalloced; i++) { shmsegs[i].u.shm_perm.mode = SHMSEG_FREE; shmsegs[i].u.shm_perm.seq = 0; #ifdef MAC mac_sysvshm_init(&shmsegs[i]); #endif } shm_last_free = 0; shm_nused = 0; shm_committed = 0; sx_init(&sysvshmsx, "sysvshmsx"); shmexit_hook = &shmexit_myhook; shmfork_hook = &shmfork_myhook; /* Set current prisons according to their allow.sysvipc. */ shm_prison_slot = osd_jail_register(NULL, methods); rsv = osd_reserve(shm_prison_slot); prison_lock(&prison0); (void)osd_jail_set_reserved(&prison0, shm_prison_slot, rsv, &prison0); prison_unlock(&prison0); rsv = NULL; sx_slock(&allprison_lock); TAILQ_FOREACH(pr, &allprison, pr_list) { if (rsv == NULL) rsv = osd_reserve(shm_prison_slot); prison_lock(pr); if ((pr->pr_allow & PR_ALLOW_SYSVIPC) && pr->pr_ref > 0) { (void)osd_jail_set_reserved(pr, shm_prison_slot, rsv, &prison0); rsv = NULL; } prison_unlock(pr); } if (rsv != NULL) osd_free_reserved(rsv); sx_sunlock(&allprison_lock); error = syscall_helper_register(shm_syscalls, SY_THR_STATIC_KLD); if (error != 0) return (error); #ifdef COMPAT_FREEBSD32 error = syscall32_helper_register(shm32_syscalls, SY_THR_STATIC_KLD); if (error != 0) return (error); #endif return (0); } static int shmunload(void) { int i; if (shm_nused > 0) return (EBUSY); #ifdef COMPAT_FREEBSD32 syscall32_helper_unregister(shm32_syscalls); #endif syscall_helper_unregister(shm_syscalls); if (shm_prison_slot != 0) osd_jail_deregister(shm_prison_slot); for (i = 0; i < shmalloced; i++) { #ifdef MAC mac_sysvshm_destroy(&shmsegs[i]); #endif /* * Objects might be still mapped into the processes * address spaces. Actual free would happen on the * last mapping destruction. */ if (shmsegs[i].u.shm_perm.mode != SHMSEG_FREE) vm_object_deallocate(shmsegs[i].object); } free(shmsegs, M_SHM); shmexit_hook = NULL; shmfork_hook = NULL; sx_destroy(&sysvshmsx); return (0); } static int sysctl_shmsegs(SYSCTL_HANDLER_ARGS) { struct shmid_kernel tshmseg; #ifdef COMPAT_FREEBSD32 struct shmid_kernel32 tshmseg32; #endif struct prison *pr, *rpr; void *outaddr; size_t outsize; int error, i; SYSVSHM_LOCK(); pr = req->td->td_ucred->cr_prison; rpr = shm_find_prison(req->td->td_ucred); error = 0; for (i = 0; i < shmalloced; i++) { if ((shmsegs[i].u.shm_perm.mode & SHMSEG_ALLOCATED) == 0 || rpr == NULL || shm_prison_cansee(rpr, &shmsegs[i]) != 0) { bzero(&tshmseg, sizeof(tshmseg)); tshmseg.u.shm_perm.mode = SHMSEG_FREE; } else { tshmseg = shmsegs[i]; if (tshmseg.cred->cr_prison != pr) tshmseg.u.shm_perm.key = IPC_PRIVATE; } #ifdef COMPAT_FREEBSD32 if (SV_CURPROC_FLAG(SV_ILP32)) { bzero(&tshmseg32, sizeof(tshmseg32)); freebsd32_ipcperm_out(&tshmseg.u.shm_perm, &tshmseg32.u.shm_perm); CP(tshmseg, tshmseg32, u.shm_segsz); CP(tshmseg, tshmseg32, u.shm_lpid); CP(tshmseg, tshmseg32, u.shm_cpid); CP(tshmseg, tshmseg32, u.shm_nattch); CP(tshmseg, tshmseg32, u.shm_atime); CP(tshmseg, tshmseg32, u.shm_dtime); CP(tshmseg, tshmseg32, u.shm_ctime); /* Don't copy object, label, or cred */ outaddr = &tshmseg32; outsize = sizeof(tshmseg32); } else #endif { tshmseg.object = NULL; tshmseg.label = NULL; tshmseg.cred = NULL; outaddr = &tshmseg; outsize = sizeof(tshmseg); } error = SYSCTL_OUT(req, outaddr, outsize); if (error != 0) break; } SYSVSHM_UNLOCK(); return (error); } static int shm_prison_check(void *obj, void *data) { struct prison *pr = obj; struct prison *prpr; struct vfsoptlist *opts = data; int error, jsys; /* * sysvshm is a jailsys integer. * It must be "disable" if the parent jail is disabled. */ error = vfs_copyopt(opts, "sysvshm", &jsys, sizeof(jsys)); if (error != ENOENT) { if (error != 0) return (error); switch (jsys) { case JAIL_SYS_DISABLE: break; case JAIL_SYS_NEW: case JAIL_SYS_INHERIT: prison_lock(pr->pr_parent); prpr = osd_jail_get(pr->pr_parent, shm_prison_slot); prison_unlock(pr->pr_parent); if (prpr == NULL) return (EPERM); break; default: return (EINVAL); } } return (0); } static int shm_prison_set(void *obj, void *data) { struct prison *pr = obj; struct prison *tpr, *orpr, *nrpr, *trpr; struct vfsoptlist *opts = data; void *rsv; int jsys, descend; /* * sysvshm controls which jail is the root of the associated segments * (this jail or same as the parent), or if the feature is available * at all. */ if (vfs_copyopt(opts, "sysvshm", &jsys, sizeof(jsys)) == ENOENT) jsys = vfs_flagopt(opts, "allow.sysvipc", NULL, 0) ? JAIL_SYS_INHERIT : vfs_flagopt(opts, "allow.nosysvipc", NULL, 0) ? JAIL_SYS_DISABLE : -1; if (jsys == JAIL_SYS_DISABLE) { prison_lock(pr); orpr = osd_jail_get(pr, shm_prison_slot); if (orpr != NULL) osd_jail_del(pr, shm_prison_slot); prison_unlock(pr); if (orpr != NULL) { if (orpr == pr) shm_prison_cleanup(pr); /* Disable all child jails as well. */ FOREACH_PRISON_DESCENDANT(pr, tpr, descend) { prison_lock(tpr); trpr = osd_jail_get(tpr, shm_prison_slot); if (trpr != NULL) { osd_jail_del(tpr, shm_prison_slot); prison_unlock(tpr); if (trpr == tpr) shm_prison_cleanup(tpr); } else { prison_unlock(tpr); descend = 0; } } } } else if (jsys != -1) { if (jsys == JAIL_SYS_NEW) nrpr = pr; else { prison_lock(pr->pr_parent); nrpr = osd_jail_get(pr->pr_parent, shm_prison_slot); prison_unlock(pr->pr_parent); } rsv = osd_reserve(shm_prison_slot); prison_lock(pr); orpr = osd_jail_get(pr, shm_prison_slot); if (orpr != nrpr) (void)osd_jail_set_reserved(pr, shm_prison_slot, rsv, nrpr); else osd_free_reserved(rsv); prison_unlock(pr); if (orpr != nrpr) { if (orpr == pr) shm_prison_cleanup(pr); if (orpr != NULL) { /* Change child jails matching the old root, */ FOREACH_PRISON_DESCENDANT(pr, tpr, descend) { prison_lock(tpr); trpr = osd_jail_get(tpr, shm_prison_slot); if (trpr == orpr) { (void)osd_jail_set(tpr, shm_prison_slot, nrpr); prison_unlock(tpr); if (trpr == tpr) shm_prison_cleanup(tpr); } else { prison_unlock(tpr); descend = 0; } } } } } return (0); } static int shm_prison_get(void *obj, void *data) { struct prison *pr = obj; struct prison *rpr; struct vfsoptlist *opts = data; int error, jsys; /* Set sysvshm based on the jail's root prison. */ prison_lock(pr); rpr = osd_jail_get(pr, shm_prison_slot); prison_unlock(pr); jsys = rpr == NULL ? JAIL_SYS_DISABLE : rpr == pr ? JAIL_SYS_NEW : JAIL_SYS_INHERIT; error = vfs_setopt(opts, "sysvshm", &jsys, sizeof(jsys)); if (error == ENOENT) error = 0; return (error); } static int shm_prison_remove(void *obj, void *data __unused) { struct prison *pr = obj; struct prison *rpr; SYSVSHM_LOCK(); prison_lock(pr); rpr = osd_jail_get(pr, shm_prison_slot); prison_unlock(pr); if (rpr == pr) shm_prison_cleanup(pr); SYSVSHM_UNLOCK(); return (0); } static void shm_prison_cleanup(struct prison *pr) { struct shmid_kernel *shmseg; int i; /* Remove any segments that belong to this jail. */ for (i = 0; i < shmalloced; i++) { shmseg = &shmsegs[i]; if ((shmseg->u.shm_perm.mode & SHMSEG_ALLOCATED) && shmseg->cred != NULL && shmseg->cred->cr_prison == pr) { shm_remove(shmseg, i); } } } SYSCTL_JAIL_PARAM_SYS_NODE(sysvshm, CTLFLAG_RW, "SYSV shared memory"); #if defined(__i386__) && (defined(COMPAT_FREEBSD4) || defined(COMPAT_43)) struct oshmid_ds { struct ipc_perm_old shm_perm; /* operation perms */ int shm_segsz; /* size of segment (bytes) */ u_short shm_cpid; /* pid, creator */ u_short shm_lpid; /* pid, last operation */ short shm_nattch; /* no. of current attaches */ time_t shm_atime; /* last attach time */ time_t shm_dtime; /* last detach time */ time_t shm_ctime; /* last change time */ void *shm_handle; /* internal handle for shm segment */ }; struct oshmctl_args { int shmid; int cmd; struct oshmid_ds *ubuf; }; static int oshmctl(struct thread *td, struct oshmctl_args *uap) { #ifdef COMPAT_43 int error = 0; struct prison *rpr; struct shmid_kernel *shmseg; struct oshmid_ds outbuf; rpr = shm_find_prison(td->td_ucred); if (rpr == NULL) return (ENOSYS); if (uap->cmd != IPC_STAT) { return (freebsd7_shmctl(td, (struct freebsd7_shmctl_args *)uap)); } SYSVSHM_LOCK(); shmseg = shm_find_segment(rpr, uap->shmid, true); if (shmseg == NULL) { SYSVSHM_UNLOCK(); return (EINVAL); } error = ipcperm(td, &shmseg->u.shm_perm, IPC_R); if (error != 0) { SYSVSHM_UNLOCK(); return (error); } #ifdef MAC error = mac_sysvshm_check_shmctl(td->td_ucred, shmseg, uap->cmd); if (error != 0) { SYSVSHM_UNLOCK(); return (error); } #endif ipcperm_new2old(&shmseg->u.shm_perm, &outbuf.shm_perm); outbuf.shm_segsz = shmseg->u.shm_segsz; outbuf.shm_cpid = shmseg->u.shm_cpid; outbuf.shm_lpid = shmseg->u.shm_lpid; outbuf.shm_nattch = shmseg->u.shm_nattch; outbuf.shm_atime = shmseg->u.shm_atime; outbuf.shm_dtime = shmseg->u.shm_dtime; outbuf.shm_ctime = shmseg->u.shm_ctime; outbuf.shm_handle = shmseg->object; SYSVSHM_UNLOCK(); return (copyout(&outbuf, uap->ubuf, sizeof(outbuf))); #else return (EINVAL); #endif } /* XXX casting to (sy_call_t *) is bogus, as usual. */ static sy_call_t *shmcalls[] = { (sy_call_t *)sys_shmat, (sy_call_t *)oshmctl, (sy_call_t *)sys_shmdt, (sy_call_t *)sys_shmget, (sy_call_t *)freebsd7_shmctl }; #ifndef _SYS_SYSPROTO_H_ /* XXX actually varargs. */ struct shmsys_args { int which; int a2; int a3; int a4; }; #endif int sys_shmsys(struct thread *td, struct shmsys_args *uap) { AUDIT_ARG_SVIPC_WHICH(uap->which); if (uap->which < 0 || uap->which >= nitems(shmcalls)) return (EINVAL); return ((*shmcalls[uap->which])(td, &uap->a2)); } #endif /* i386 && (COMPAT_FREEBSD4 || COMPAT_43) */ #ifdef COMPAT_FREEBSD32 int freebsd32_shmsys(struct thread *td, struct freebsd32_shmsys_args *uap) { #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) AUDIT_ARG_SVIPC_WHICH(uap->which); switch (uap->which) { case 0: { /* shmat */ struct shmat_args ap; ap.shmid = uap->a2; ap.shmaddr = PTRIN(uap->a3); ap.shmflg = uap->a4; return (sysent[SYS_shmat].sy_call(td, &ap)); } case 2: { /* shmdt */ struct shmdt_args ap; ap.shmaddr = PTRIN(uap->a2); return (sysent[SYS_shmdt].sy_call(td, &ap)); } case 3: { /* shmget */ struct shmget_args ap; ap.key = uap->a2; ap.size = uap->a3; ap.shmflg = uap->a4; return (sysent[SYS_shmget].sy_call(td, &ap)); } case 4: { /* shmctl */ struct freebsd7_freebsd32_shmctl_args ap; ap.shmid = uap->a2; ap.cmd = uap->a3; ap.buf = PTRIN(uap->a4); return (freebsd7_freebsd32_shmctl(td, &ap)); } case 1: /* oshmctl */ default: return (EINVAL); } #else return (nosys(td, NULL)); #endif } #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) int freebsd7_freebsd32_shmctl(struct thread *td, struct freebsd7_freebsd32_shmctl_args *uap) { int error; union { struct shmid_ds shmid_ds; struct shm_info shm_info; struct shminfo shminfo; } u; union { struct shmid_ds32_old shmid_ds32; struct shm_info32 shm_info32; struct shminfo32 shminfo32; } u32; size_t sz; if (uap->cmd == IPC_SET) { if ((error = copyin(uap->buf, &u32.shmid_ds32, sizeof(u32.shmid_ds32)))) goto done; freebsd32_ipcperm_old_in(&u32.shmid_ds32.shm_perm, &u.shmid_ds.shm_perm); CP(u32.shmid_ds32, u.shmid_ds, shm_segsz); CP(u32.shmid_ds32, u.shmid_ds, shm_lpid); CP(u32.shmid_ds32, u.shmid_ds, shm_cpid); CP(u32.shmid_ds32, u.shmid_ds, shm_nattch); CP(u32.shmid_ds32, u.shmid_ds, shm_atime); CP(u32.shmid_ds32, u.shmid_ds, shm_dtime); CP(u32.shmid_ds32, u.shmid_ds, shm_ctime); } error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&u, &sz); if (error) goto done; /* Cases in which we need to copyout */ switch (uap->cmd) { case IPC_INFO: CP(u.shminfo, u32.shminfo32, shmmax); CP(u.shminfo, u32.shminfo32, shmmin); CP(u.shminfo, u32.shminfo32, shmmni); CP(u.shminfo, u32.shminfo32, shmseg); CP(u.shminfo, u32.shminfo32, shmall); error = copyout(&u32.shminfo32, uap->buf, sizeof(u32.shminfo32)); break; case SHM_INFO: CP(u.shm_info, u32.shm_info32, used_ids); CP(u.shm_info, u32.shm_info32, shm_rss); CP(u.shm_info, u32.shm_info32, shm_tot); CP(u.shm_info, u32.shm_info32, shm_swp); CP(u.shm_info, u32.shm_info32, swap_attempts); CP(u.shm_info, u32.shm_info32, swap_successes); error = copyout(&u32.shm_info32, uap->buf, sizeof(u32.shm_info32)); break; case SHM_STAT: case IPC_STAT: memset(&u32.shmid_ds32, 0, sizeof(u32.shmid_ds32)); freebsd32_ipcperm_old_out(&u.shmid_ds.shm_perm, &u32.shmid_ds32.shm_perm); if (u.shmid_ds.shm_segsz > INT32_MAX) u32.shmid_ds32.shm_segsz = INT32_MAX; else CP(u.shmid_ds, u32.shmid_ds32, shm_segsz); CP(u.shmid_ds, u32.shmid_ds32, shm_lpid); CP(u.shmid_ds, u32.shmid_ds32, shm_cpid); CP(u.shmid_ds, u32.shmid_ds32, shm_nattch); CP(u.shmid_ds, u32.shmid_ds32, shm_atime); CP(u.shmid_ds, u32.shmid_ds32, shm_dtime); CP(u.shmid_ds, u32.shmid_ds32, shm_ctime); u32.shmid_ds32.shm_internal = 0; error = copyout(&u32.shmid_ds32, uap->buf, sizeof(u32.shmid_ds32)); break; } done: if (error) { /* Invalidate the return value */ td->td_retval[0] = -1; } return (error); } #endif int freebsd32_shmctl(struct thread *td, struct freebsd32_shmctl_args *uap) { int error; union { struct shmid_ds shmid_ds; struct shm_info shm_info; struct shminfo shminfo; } u; union { struct shmid_ds32 shmid_ds32; struct shm_info32 shm_info32; struct shminfo32 shminfo32; } u32; size_t sz; if (uap->cmd == IPC_SET) { if ((error = copyin(uap->buf, &u32.shmid_ds32, sizeof(u32.shmid_ds32)))) goto done; freebsd32_ipcperm_in(&u32.shmid_ds32.shm_perm, &u.shmid_ds.shm_perm); CP(u32.shmid_ds32, u.shmid_ds, shm_segsz); CP(u32.shmid_ds32, u.shmid_ds, shm_lpid); CP(u32.shmid_ds32, u.shmid_ds, shm_cpid); CP(u32.shmid_ds32, u.shmid_ds, shm_nattch); CP(u32.shmid_ds32, u.shmid_ds, shm_atime); CP(u32.shmid_ds32, u.shmid_ds, shm_dtime); CP(u32.shmid_ds32, u.shmid_ds, shm_ctime); } error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&u, &sz); if (error) goto done; /* Cases in which we need to copyout */ switch (uap->cmd) { case IPC_INFO: CP(u.shminfo, u32.shminfo32, shmmax); CP(u.shminfo, u32.shminfo32, shmmin); CP(u.shminfo, u32.shminfo32, shmmni); CP(u.shminfo, u32.shminfo32, shmseg); CP(u.shminfo, u32.shminfo32, shmall); error = copyout(&u32.shminfo32, uap->buf, sizeof(u32.shminfo32)); break; case SHM_INFO: CP(u.shm_info, u32.shm_info32, used_ids); CP(u.shm_info, u32.shm_info32, shm_rss); CP(u.shm_info, u32.shm_info32, shm_tot); CP(u.shm_info, u32.shm_info32, shm_swp); CP(u.shm_info, u32.shm_info32, swap_attempts); CP(u.shm_info, u32.shm_info32, swap_successes); error = copyout(&u32.shm_info32, uap->buf, sizeof(u32.shm_info32)); break; case SHM_STAT: case IPC_STAT: freebsd32_ipcperm_out(&u.shmid_ds.shm_perm, &u32.shmid_ds32.shm_perm); if (u.shmid_ds.shm_segsz > INT32_MAX) u32.shmid_ds32.shm_segsz = INT32_MAX; else CP(u.shmid_ds, u32.shmid_ds32, shm_segsz); CP(u.shmid_ds, u32.shmid_ds32, shm_lpid); CP(u.shmid_ds, u32.shmid_ds32, shm_cpid); CP(u.shmid_ds, u32.shmid_ds32, shm_nattch); CP(u.shmid_ds, u32.shmid_ds32, shm_atime); CP(u.shmid_ds, u32.shmid_ds32, shm_dtime); CP(u.shmid_ds, u32.shmid_ds32, shm_ctime); error = copyout(&u32.shmid_ds32, uap->buf, sizeof(u32.shmid_ds32)); break; } done: if (error) { /* Invalidate the return value */ td->td_retval[0] = -1; } return (error); } #endif #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) - -#ifndef CP -#define CP(src, dst, fld) do { (dst).fld = (src).fld; } while (0) -#endif #ifndef _SYS_SYSPROTO_H_ struct freebsd7_shmctl_args { int shmid; int cmd; struct shmid_ds_old *buf; }; #endif int freebsd7_shmctl(struct thread *td, struct freebsd7_shmctl_args *uap) { int error; struct shmid_ds_old old; struct shmid_ds buf; size_t bufsz; /* * The only reason IPC_INFO, SHM_INFO, SHM_STAT exists is to support * Linux binaries. If we see the call come through the FreeBSD ABI, * return an error back to the user since we do not to support this. */ if (uap->cmd == IPC_INFO || uap->cmd == SHM_INFO || uap->cmd == SHM_STAT) return (EINVAL); /* IPC_SET needs to copyin the buffer before calling kern_shmctl */ if (uap->cmd == IPC_SET) { if ((error = copyin(uap->buf, &old, sizeof(old)))) goto done; ipcperm_old2new(&old.shm_perm, &buf.shm_perm); CP(old, buf, shm_segsz); CP(old, buf, shm_lpid); CP(old, buf, shm_cpid); CP(old, buf, shm_nattch); CP(old, buf, shm_atime); CP(old, buf, shm_dtime); CP(old, buf, shm_ctime); } error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz); if (error) goto done; /* Cases in which we need to copyout */ switch (uap->cmd) { case IPC_STAT: memset(&old, 0, sizeof(old)); ipcperm_new2old(&buf.shm_perm, &old.shm_perm); if (buf.shm_segsz > INT_MAX) old.shm_segsz = INT_MAX; else CP(buf, old, shm_segsz); CP(buf, old, shm_lpid); CP(buf, old, shm_cpid); if (buf.shm_nattch > SHRT_MAX) old.shm_nattch = SHRT_MAX; else CP(buf, old, shm_nattch); CP(buf, old, shm_atime); CP(buf, old, shm_dtime); CP(buf, old, shm_ctime); old.shm_internal = NULL; error = copyout(&old, uap->buf, sizeof(old)); break; } done: if (error) { /* Invalidate the return value */ td->td_retval[0] = -1; } return (error); } #endif /* COMPAT_FREEBSD4 || COMPAT_FREEBSD5 || COMPAT_FREEBSD6 || COMPAT_FREEBSD7 */ static int sysvshm_modload(struct module *module, int cmd, void *arg) { int error = 0; switch (cmd) { case MOD_LOAD: error = shminit(); if (error != 0) shmunload(); break; case MOD_UNLOAD: error = shmunload(); break; case MOD_SHUTDOWN: break; default: error = EINVAL; break; } return (error); } static moduledata_t sysvshm_mod = { "sysvshm", &sysvshm_modload, NULL }; DECLARE_MODULE(sysvshm, sysvshm_mod, SI_SUB_SYSV_SHM, SI_ORDER_FIRST); MODULE_VERSION(sysvshm, 1); Index: stable/12/sys/sys/abi_compat.h =================================================================== --- stable/12/sys/sys/abi_compat.h (nonexistent) +++ stable/12/sys/sys/abi_compat.h (revision 360446) @@ -0,0 +1,77 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2001 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _COMPAT_H_ +#define _COMPAT_H_ + +/* + * Helper macros for translating objects between different ABIs. + */ + +#define PTRIN(v) (void *)(uintptr_t)(v) +#define PTROUT(v) (uintptr_t)(v) + +#define CP(src, dst, fld) do { \ + (dst).fld = (src).fld; \ +} while (0) + +#define CP2(src, dst, sfld, dfld) do { \ + (dst).dfld = (src).sfld; \ +} while (0) + +#define PTRIN_CP(src, dst, fld) do { \ + (dst).fld = PTRIN((src).fld); \ +} while (0) + +#define PTROUT_CP(src, dst, fld) do { \ + (dst).fld = PTROUT((src).fld); \ +} while (0) + +#define TV_CP(src, dst, fld) do { \ + CP((src).fld, (dst).fld, tv_sec); \ + CP((src).fld, (dst).fld, tv_usec); \ +} while (0) + +#define TS_CP(src, dst, fld) do { \ + CP((src).fld, (dst).fld, tv_sec); \ + CP((src).fld, (dst).fld, tv_nsec); \ +} while (0) + +#define ITS_CP(src, dst) do { \ + TS_CP((src), (dst), it_interval); \ + TS_CP((src), (dst), it_value); \ +} while (0) + +#define BT_CP(src, dst, fld) do { \ + CP((src).fld, (dst).fld, sec); \ + *(uint64_t *)&(dst).fld.frac[0] = (src).fld.frac; \ +} while (0) + +#endif /* !_COMPAT_H_ */ Property changes on: stable/12/sys/sys/abi_compat.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/12 =================================================================== --- stable/12 (revision 360445) +++ stable/12 (revision 360446) Property changes on: stable/12 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r359937