diff --git a/Makefile.target b/Makefile.target --- a/Makefile.target +++ b/Makefile.target @@ -95,7 +95,7 @@ ifdef CONFIG_BSD_USER QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ARCH) obj-y += bsd-user/ -obj-y += gdbstub.o user-exec.o +obj-y += gdbstub.o thunk.o user-exec.o endif #CONFIG_BSD_USER diff --git a/bsd-user/freebsd/ioccom.h b/bsd-user/freebsd/ioccom.h new file mode 100644 index 0000000..830e377 --- /dev/null +++ b/bsd-user/freebsd/ioccom.h @@ -0,0 +1,34 @@ +#ifndef _FREEBSD_IOCCOM_H_ +#define _FREEBSD_IOCCOM_H_ +/* + * Ioctl's have the command encoded in the lower word, and the size of + * any in or out parameters in the upper word. The high 3 bits of the + * upper word are used to encode the in/out status of the parameter. + */ +/* number of bits for ioctl size */ +#define TARGET_IOCPARM_SHIFT 13 + +/* parameter length mask */ +#define TARGET_IOCPARM_MASK ((1 << TARGET_IOCPARM_SHIFT) - 1) + +#define TARGET_IOCPARM_LEN(x) (((x) >> 16) & TARGET_IOCPARM_MASK) +#define TARGET_IOCBASECMD(x) ((x) & ~(TARGET_IOCPARM_MASK << 16)) +#define TARGET_IOCGROUP(x) (((x) >> 8) & 0xff) + +#define TARGET_IOCPARM_MAX (1 << TARGET_IOCPARM_SHIFT) /* max size of ioctl */ +#define TARGET_IOC_VOID 0x20000000 /* no parameters */ +#define TARGET_IOC_OUT 0x40000000 /* copy out parameters */ +#define TARGET_IOC_IN 0x80000000 /* copy in parameters */ +#define TARGET_IOC_INOUT (TARGET_IOC_IN|TARGET_IOC_OUT) +#define TARGET_IOC_DIRMASK (TARGET_IOC_VOID|TARGET_IOC_OUT|TARGET_IOC_IN) + +#define TARGET_IOC(inout,group,num,len) ((abi_ulong) \ + ((inout) | (((len) & TARGET_IOCPARM_MASK) << 16) | ((group) << 8) \ + | (num))) +#define TARGET_IO(g,n) TARGET_IOC(IOC_VOID, (g), (n), 0) +#define TARGET_IOWINT(g,n) TARGET_IOC(IOC_VOID, (g), (n), sizeof(int)) +#define TARGET_IOR(g,n,t) TARGET_IOC(IOC_OUT, (g), (n), sizeof(t)) +#define TARGET_IOW(g,n,t) TARGET_IOC(IOC_IN, (g), (n), sizeof(t)) +/* this should be _IORW, but stdio got there first */ +#define TARGET_IOWR(g,n,t) TARGET_IOC(IOC_INOUT, (g), (n), sizeof(t)) +#endif diff --git a/bsd-user/freebsd/ioctl.h b/bsd-user/freebsd/ioctl.h new file mode 100644 index 0000000..67c5583 --- /dev/null +++ b/bsd-user/freebsd/ioctl.h @@ -0,0 +1,35 @@ +#ifndef _FREEBSD_IOCTL_H_ +#define _FREEBSD_IOCTL_H_ + +/* sys/ttycom.h tty(4) */ +IOCTL(TIOCSETD, IOC_W, MK_PTR(TYPE_INT)) +IOCTL(TIOCGETD, IOC_R, MK_PTR(TYPE_INT)) +IOCTL(TIOCSBRK, IOC_, TYPE_NULL) +IOCTL(TIOCCBRK, IOC_, TYPE_NULL) +IOCTL(TIOCSDTR, IOC_, TYPE_NULL) +IOCTL(TIOCCDTR, IOC_, TYPE_NULL) +IOCTL(TIOCGPGRP, IOC_W, MK_PTR(TYPE_INT)) +IOCTL(TIOCSPGRP, IOC_R, MK_PTR(TYPE_INT)) +IOCTL(TIOCGETA, IOC_R, MK_PTR(MK_STRUCT(STRUCT_termios))) +IOCTL(TIOCSETA, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios))) +IOCTL(TIOCSETAW, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios))) +IOCTL(TIOCSETAF, IOC_W, MK_PTR(MK_STRUCT(STRUCT_termios))) +IOCTL(TIOCOUTQ, IOC_R, MK_PTR(TYPE_INT)) +IOCTL(TIOCSTI, IOC_W, MK_PTR(TYPE_CHAR)) +IOCTL(TIOCNOTTY, IOC_, TYPE_NULL) +IOCTL(TIOCSTOP, IOC_, TYPE_NULL) +IOCTL(TIOCSTART, IOC_, TYPE_NULL) +IOCTL(TIOCSCTTY, IOC_, TYPE_NULL) +IOCTL(TIOCDRAIN, IOC_, TYPE_NULL) +IOCTL(TIOCEXCL, IOC_, TYPE_NULL) +IOCTL(TIOCNXCL, IOC_, TYPE_NULL) +IOCTL(TIOCFLUSH, IOC_W, MK_PTR(TYPE_INT)) +IOCTL(TIOCGWINSZ, IOC_R, MK_PTR(MK_STRUCT(STRUCT_winsize))) +IOCTL(TIOCSWINSZ, IOC_W, MK_PTR(MK_STRUCT(STRUCT_winsize))) +IOCTL(TIOCCONS, IOC_W, MK_PTR(TYPE_INT)) +IOCTL(TIOCMSET, IOC_W, MK_PTR(TYPE_INT)) +IOCTL(TIOCMGET, IOC_R, MK_PTR(TYPE_INT)) +IOCTL(TIOCMBIS, IOC_W, MK_PTR(TYPE_INT)) +IOCTL(TIOCMBIC, IOC_W, MK_PTR(TYPE_INT)) + +#endif diff --git a/bsd-user/freebsd/strace.list b/bsd-user/freebsd/strace.list index b09f766..bcdd931 100644 --- a/bsd-user/freebsd/strace.list +++ b/bsd-user/freebsd/strace.list @@ -2,6 +2,7 @@ { TARGET_FREEBSD_NR___semctl, "__semctl", NULL, NULL, NULL }, { TARGET_FREEBSD_NR___syscall, "__syscall", NULL, NULL, NULL }, { TARGET_FREEBSD_NR___sysctl, "__sysctl", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR__umtx_op, "_umtx_op", "%s(%#x, %d, %d, %#x, %#x)", NULL, NULL }, { TARGET_FREEBSD_NR_accept, "accept", "%s(%d,%#x,%#x)", NULL, NULL }, { TARGET_FREEBSD_NR_access, "access", "%s(\"%s\",%#o)", NULL, NULL }, { TARGET_FREEBSD_NR_acct, "acct", NULL, NULL, NULL }, diff --git a/bsd-user/freebsd/syscall_types.h b/bsd-user/freebsd/syscall_types.h new file mode 100644 index 0000000..6e43400 --- /dev/null +++ b/bsd-user/freebsd/syscall_types.h @@ -0,0 +1,8 @@ +#ifndef _FREEBSD_SYSCALL_TYPES_H_ +#define _FREEBSD_SYSCALL_TYPES_H_ + +STRUCT_SPECIAL(termios) + +STRUCT(winsize, TYPE_SHORT, TYPE_SHORT, TYPE_SHORT, TYPE_SHORT) + +#endif diff --git a/bsd-user/freebsd/ttycom.h b/bsd-user/freebsd/ttycom.h new file mode 100644 index 0000000..699c282 --- /dev/null +++ b/bsd-user/freebsd/ttycom.h @@ -0,0 +1,238 @@ +#ifndef _FREEBSD_TTYCOM_H_ +#define _FREEBSD_TTYCOM_H_ + +#include "ioccom.h" + +/* From sys/ttycom.h and sys/_termios.h */ + +#define TARGET_VEOF 0 /* ICANON */ +#define TARGET_VEOL 1 /* ICANON */ +#define TARGET_VEOL2 2 /* ICANON together with IEXTEN */ +#define TARGET_VERASE 3 /* ICANON */ +#define TARGET_VWERASE 4 /* ICANON together with IEXTEN */ +#define TARGET_VKILL 5 /* ICANON */ +#define TARGET_VREPRINT 6 /* ICANON together with IEXTEN */ +#define TARGET_VERASE2 7 /* ICANON */ +#define TARGET_VINTR 8 /* ISIG */ +#define TARGET_VQUIT 9 /* ISIG */ +#define TARGET_VSUSP 10 /* ISIG */ +#define TARGET_VDSUSP 11 /* ISIG together with IEXTEN */ +#define TARGET_VSTART 12 /* IXON, IXOFF */ +#define TARGET_VSTOP 13 /* IXON, IXOFF */ +#define TARGET_VLNEXT 14 /* IEXTEN */ +#define TARGET_VDISCARD 15 /* IEXTEN */ +#define TARGET_VMIN 16 /* !ICANON */ +#define TARGET_VTIME 17 /* !ICANON */ +#define TARGET_VSTATUS 18 /* ICANON together with IEXTEN */ +/* 19 spare 2 */ +#define TARGET_NCCS 20 + +/* + * Input flags - software input processing + */ +#define TARGET_IGNBRK 0x00000001 /* ignore BREAK condition */ +#define TARGET_BRKINT 0x00000002 /* map BREAK to SIGINTR */ +#define TARGET_IGNPAR 0x00000004 /* ignore (discard) parity errors */ +#define TARGET_PARMRK 0x00000008 /* mark parity and framing errors */ +#define TARGET_INPCK 0x00000010 /* enable checking of parity errors */ +#define TARGET_ISTRIP 0x00000020 /* strip 8th bit off chars */ +#define TARGET_INLCR 0x00000040 /* map NL into CR */ +#define TARGET_IGNCR 0x00000080 /* ignore CR */ +#define TARGET_ICRNL 0x00000100 /* map CR to NL (ala CRMOD) */ +#define TARGET_IXON 0x00000200 /* enable output flow control */ +#define TARGET_IXOFF 0x00000400 /* enable input flow control */ +#define TARGET_IXANY 0x00000800 /* any char will restart after stop */ +#define TARGET_IMAXBEL 0x00002000 /* ring bell on input queue full */ + +/* + * Output flags - software output processing + */ +#define TARGET_OPOST 0x00000001 /* enable following output processing */ +#define TARGET_ONLCR 0x00000002 /* map NL to CR-NL (ala CRMOD) */ +#define TARGET_TABDLY 0x00000004 /* tab delay mask */ +#define TARGET_TAB0 0x00000000 /* no tab delay and expansion */ +#define TARGET_TAB3 0x00000004 /* expand tabs to spaces */ +#define TARGET_ONOEOT 0x00000008 /* discard EOT's (^D) on output) */ +#define TARGET_OCRNL 0x00000010 /* map CR to NL on output */ +#define TARGET_ONOCR 0x00000020 /* no CR output at column 0 */ +#define TARGET_ONLRET 0x00000040 /* NL performs CR function */ + +/* + * Control flags - hardware control of terminal + */ +#define TARGET_CIGNORE 0x00000001 /* ignore control flags */ +#define TARGET_CSIZE 0x00000300 /* character size mask */ +#define TARGET_CS5 0x00000000 /* 5 bits (pseudo) */ +#define TARGET_CS6 0x00000100 /* 6 bits */ +#define TARGET_CS7 0x00000200 /* 7 bits */ +#define TARGET_CS8 0x00000300 /* 8 bits */ +#define TARGET_CSTOPB 0x00000400 /* send 2 stop bits */ +#define TARGET_CREAD 0x00000800 /* enable receiver */ +#define TARGET_PARENB 0x00001000 /* parity enable */ +#define TARGET_PARODD 0x00002000 /* odd parity, else even */ +#define TARGET_HUPCL 0x00004000 /* hang up on last close */ +#define TARGET_CLOCAL 0x00008000 /* ignore modem status lines */ +#define TARGET_CCTS_OFLOW 0x00010000 /* CTS flow control of output */ +#define TARGET_CRTSCTS (TARGET_CCTS_OFLOW | TARGET_CRTS_IFLOW) +#define TARGET_CRTS_IFLOW 0x00020000 /* RTS flow control of input */ +#define TARGET_CDTR_IFLOW 0x00040000 /* DTR flow control of input */ +#define TARGET_CDSR_OFLOW 0x00080000 /* DSR flow control of output */ +#define TARGET_CCAR_OFLOW 0x00100000 /* DCD flow control of output */ + +/* + * "Local" flags - dumping ground for other state + */ +#define TARGET_ECHOKE 0x00000001 /* visual erase for line kill */ +#define TARGET_ECHOE 0x00000002 /* visually erase chars */ +#define TARGET_ECHOK 0x00000004 /* echo NL after line kill */ +#define TARGET_ECHO 0x00000008 /* enable echoing */ +#define TARGET_ECHONL 0x00000010 /* echo NL even if ECHO is off */ +#define TARGET_ECHOPRT 0x00000020 /* visual erase mode for hardcopy */ +#define TARGET_ECHOCTL 0x00000040 /* echo control chars as ^(Char) */ +#define TARGET_ISIG 0x00000080 /* enable signals INTR, QUIT, [D]SUSP */ +#define TARGET_ICANON 0x00000100 /* canonicalize input lines */ +#define TARGET_ALTWERASE 0x00000200 /* use alternate WERASE algorithm */ +#define TARGET_IEXTEN 0x00000400 /* enable DISCARD and LNEXT */ +#define TARGET_EXTPROC 0x00000800 /* external processing */ +#define TARGET_TOSTOP 0x00400000 /* stop background jobs from output */ +#define TARGET_FLUSHO 0x00800000 /* output being flushed (state) */ +#define TARGET_NOKERNINFO 0x02000000 /* no kernel output from VSTATUS */ +#define TARGET_PENDIN 0x20000000 /* XXX retype pending input (state) */ +#define TARGET_NOFLSH 0x80000000 /* don't flush after interrupt */ + +struct target_termios { + uint32_t c_iflag; /* input flags */ + uint32_t c_oflag; /* output flags */ + uint32_t c_cflag; /* control flags */ + uint32_t c_lflag; /* local flags */ + uint8_t c_cc[TARGET_NCCS]; /* control chars */ + uint32_t c_ispeed; /* input speed */ + uint32_t c_ospeed; /* output speed */ +}; + + +struct target_winsize { + uint16_t ws_row; /* rows, in characters */ + uint16_t ws_col; /* columns, in characters */ + uint16_t ws_xpixel; /* horizontal size, pixels */ + uint16_t ws_ypixel; /* vertical size, pixels */ +}; + + /* 0-2 compat */ + /* 3-7 unused */ + /* 8-10 compat */ + /* 11-12 unused */ +#define TARGET_TIOCEXCL TARGET_IO('t', 13) /* set exclusive use of tty */ +#define TARGET_TIOCNXCL TARGET_IO('t', 14) /* reset exclusive use of tty */ +#define TARGET_TIOCGPTN TARGET_IOR('t', 15, int) /* Get pts number. */ +#define TARGET_TIOCFLUSH TARGET_IOW('t', 16, int) /* flush buffers */ + /* 17-18 compat */ +/* get termios struct */ +#define TARGET_TIOCGETA TARGET_IOR('t', 19, struct target_termios) +/* set termios struct */ +#define TARGET_TIOCSETA TARGET_IOW('t', 20, struct target_termios) +/* drain output, set */ +#define TARGET_TIOCSETAW TARGET_IOW('t', 21, struct target_termios) +/* drn out, fls in, set */ +#define TARGET_TIOCSETAF TARGET_IOW('t', 22, struct target_termios) + /* 23-25 unused */ +#define TARGET_TIOCGETD TARGET_IOR('t', 26, int) /* get line discipline */ +#define TARGET_TIOCSETD TARGET_IOW('t', 27, int) /* set line discipline */ +#define TARGET_TIOCPTMASTER TARGET_IO('t', 28) /* pts master validation */ + /* 29-85 unused */ +/* get ttywait timeout */ +#define TARGET_TIOCGDRAINWAIT TARGET_IOR('t', 86, int) +/* set ttywait timeout */ +#define TARGET_TIOCSDRAINWAIT TARGET_IOW('t', 87, int) + /* 88 unused */ + /* 89-91 conflicts: tun and tap */ +/* enable/get timestamp of last input event */ +#define TARGET_TIOCTIMESTAMP TARGET_IOR('t', 89, struct target_timeval) +/* modem: get wait on close */ +#define TARGET_TIOCMGDTRWAIT TARGET_IOR('t', 90, int) +/* modem: set wait on close */ +#define TARGET_TIOCMSDTRWAIT TARGET_IOW('t', 91, int) + /* 92-93 tun and tap */ + /* 94-97 conflicts: tun and tap */ +/* wait till output drained */ +#define TARGET_TIOCDRAIN TARGET_IO('t', 94) + /* pty: generate signal */ +#define TARGET_TIOCSIG TARGET_IOWINT('t', 95) +/* pty: external processing */ +#define TARGET_TIOCEXT TARGET_IOW('t', 96, int) +/* become controlling tty */ +#define TARGET_TIOCSCTTY TARGET_IO('t', 97) +/* become virtual console */ +#define TARGET_TIOCCONS TARGET_IOW('t', 98, int) +/* get session id */ +#define TARGET_TIOCGSID TARGET_IOR('t', 99, int) + /* 100 unused */ +/* simulate ^T status message */ +#define TARGET_TIOCSTAT TARGET_IO('t', 101) + /* pty: set/clr usr cntl mode */ +#define TARGET_TIOCUCNTL TARGET_IOW('t', 102, int) +/* usr cntl op "n" */ +#define TARGET_TIOCCMD(n) TARGET_IO('u', n) +/* set window size */ +#define TARGET_TIOCSWINSZ TARGET_IOW('t', 103, struct target_winsize) +/* get window size */ +#define TARGET_TIOCGWINSZ TARGET_IOR('t', 104, struct target_winsize) + /* 105 unused */ +/* get all modem bits */ +#define TARGET_TIOCMGET TARGET_IOR('t', 106, int) +#define TARGET_TIOCM_LE 0001 /* line enable */ +#define TARGET_TIOCM_DTR 0002 /* data terminal ready */ +#define TARGET_TIOCM_RTS 0004 /* request to send */ +#define TARGET_TIOCM_ST 0010 /* secondary transmit */ +#define TARGET_TIOCM_SR 0020 /* secondary receive */ +#define TARGET_TIOCM_CTS 0040 /* clear to send */ +#define TARGET_TIOCM_DCD 0100 /* data carrier detect */ +#define TARGET_TIOCM_RI 0200 /* ring indicate */ +#define TARGET_TIOCM_DSR 0400 /* data set ready */ +#define TARGET_TIOCM_CD TARGET_TIOCM_DCD +#define TARGET_TIOCM_CAR TARGET_TIOCM_DCD +#define TARGET_TIOCM_RNG TARGET_TIOCM_RI +#define TARGET_TIOCMBIC TARGET_IOW('t', 107, int) /* bic modem bits */ +#define TARGET_TIOCMBIS TARGET_IOW('t', 108, int) /* bis modem bits */ +#define TARGET_TIOCMSET TARGET_IOW('t', 109, int) /* set all modem bits */ +/* start output, like ^Q */ +#define TARGET_TIOCSTART TARGET_IO('t', 110) +/* stop output, like ^S */ +#define TARGET_TIOCSTOP TARGET_IO('t', 111) +/* pty: set/clear packet mode */ +#define TARGET_TIOCPKT TARGET_IOW('t', 112, int) +#define TARGET_TIOCPKT_DATA 0x00 /* data packet */ +#define TARGET_TIOCPKT_FLUSHREAD 0x01 /* flush packet */ +#define TARGET_TIOCPKT_FLUSHWRITE 0x02 /* flush packet */ +#define TARGET_TIOCPKT_STOP 0x04 /* stop output */ +#define TARGET_TIOCPKT_START 0x08 /* start output */ +#define TARGET_TIOCPKT_NOSTOP 0x10 /* no more ^S, ^Q */ +#define TARGET_TIOCPKT_DOSTOP 0x20 /* now do ^S ^Q */ +#define TARGET_TIOCPKT_IOCTL 0x40 /* state change of pty + driver */ +#define TARGET_TIOCNOTTY TARGET_IO('t', 113) /* void tty + association */ +#define TARGET_TIOCSTI TARGET_IOW('t', 114, char) /* simulate + terminal input */ +#define TARGET_TIOCOUTQ TARGET_IOR('t', 115, int) /* output queue size */ + /* 116-117 compat */ +#define TARGET_TIOCSPGRP TARGET_IOW('t', 118, int) /* set pgrp of tty */ +#define TARGET_TIOCGPGRP TARGET_IOR('t', 119, int) /* get pgrp of tty */ +#define TARGET_TIOCCDTR TARGET_IO('t', 120) /* clear data terminal + ready */ +#define TARGET_TIOCSDTR TARGET_IO('t', 121) /* set data terminal + ready */ +#define TARGET_TIOCCBRK TARGET_IO('t', 122) /* clear break bit */ +#define TARGET_TIOCSBRK TARGET_IO('t', 123) /* set break bit */ + /* 124-127 compat */ + +#define TARGET_TTYDISC 0 /* termios tty line + discipline */ +#define TARGET_SLIPDISC 4 /* serial IP discipline */ +#define TARGET_PPPDISC 5 /* PPP discipline */ +#define TARGET_NETGRAPHDISC 6 /* Netgraph tty node + discipline */ +#define TARGET_H4DISC 7 /* Netgraph Bluetooth H4 + discipline */ + +#endif /* _FREEBSD_TTYCOM_H_ */ diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 9d4edbf..e3bcc57 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -23,6 +23,7 @@ extern enum BSDType bsd_type; abi_long memcpy_to_target(abi_ulong dest, const void *src, unsigned long len); +#include "exec/user/thunk.h" #include "syscall_defs.h" #include "syscall.h" #include "target_vmparam.h" diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c index bde9ee9..89ce296 100644 --- a/bsd-user/syscall.c +++ b/bsd-user/syscall.c @@ -47,6 +47,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include #endif @@ -61,6 +67,10 @@ #include "qemu.h" #include "qemu-common.h" +#ifdef __FreeBSD__ +#include "freebsd/ttycom.h" +#endif + //#define DEBUG @@ -791,7 +801,7 @@ host_to_target_waitstatus(int status) } static inline abi_long -copy_from_user_timeval(struct timeval *tv, abi_ulong target_tv_addr) +target_to_host_timeval(struct timeval *tv, abi_ulong target_tv_addr) { struct target_freebsd_timeval *target_tv; @@ -804,6 +814,33 @@ copy_from_user_timeval(struct timeval *tv, abi_ulong target_tv_addr) } static inline abi_long +target_to_host_timex(struct timex *host_tx, abi_ulong target_tx_addr) +{ + struct target_timex *target_tx; + + if (!lock_user_struct(VERIFY_READ, target_tx, target_tx_addr, 0)) + return (-TARGET_EFAULT); + __get_user(host_tx->modes, &target_tx->modes); + __get_user(host_tx->offset, &target_tx->offset); + __get_user(host_tx->freq, &target_tx->freq); + __get_user(host_tx->maxerror, &target_tx->maxerror); + __get_user(host_tx->esterror, &target_tx->esterror); + __get_user(host_tx->status, &target_tx->status); + __get_user(host_tx->constant, &target_tx->constant); + __get_user(host_tx->precision, &target_tx->precision); + __get_user(host_tx->ppsfreq, &target_tx->ppsfreq); + __get_user(host_tx->jitter, &target_tx->jitter); + __get_user(host_tx->shift, &target_tx->shift); + __get_user(host_tx->stabil, &target_tx->stabil); + __get_user(host_tx->jitcnt, &target_tx->jitcnt); + __get_user(host_tx->calcnt, &target_tx->calcnt); + __get_user(host_tx->errcnt, &target_tx->errcnt); + __get_user(host_tx->stbcnt, &target_tx->stbcnt); + unlock_user_struct(target_tx, target_tx_addr, 1); + return (0); +} + +static inline abi_long target_to_host_timespec(struct timespec *ts, abi_ulong target_ts_addr) { struct target_freebsd_timespec *target_ts; @@ -817,7 +854,7 @@ target_to_host_timespec(struct timespec *ts, abi_ulong target_ts_addr) } static inline abi_long -fbsd_copy_to_user_timeval(struct timeval *tv, abi_ulong target_tv_addr) +host_to_target_timeval(struct timeval *tv, abi_ulong target_tv_addr) { struct target_freebsd_timeval *target_tv; @@ -841,6 +878,23 @@ host_to_target_timespec(abi_ulong target_ts_addr, struct timespec *ts) unlock_user_struct(target_ts, target_ts_addr, 1); return (0); } + +static inline abi_long +host_to_target_ntptimeval(abi_ulong target_ntv_addr, struct ntptimeval *ntv) +{ + struct target_ntptimeval *target_ntv; + + if (!lock_user_struct(VERIFY_WRITE, target_ntv, target_ntv_addr, 0)) + return (-TARGET_EFAULT); + __put_user(ntv->time.tv_sec, &target_ntv->time.tv_sec); + __put_user(ntv->time.tv_nsec, &target_ntv->time.tv_nsec); + __put_user(ntv->maxerror, &target_ntv->maxerror); + __put_user(ntv->esterror, &target_ntv->esterror); + __put_user(ntv->tai, &target_ntv->tai); + __put_user(ntv->time_state, &target_ntv->time_state); + return (0); +} + static inline abi_ulong copy_from_user_fdset(fd_set *fds, abi_ulong target_fds_addr, int n) { @@ -1251,7 +1305,7 @@ do_freebsd_select(int n, abi_ulong rfd_addr, abi_ulong wfd_addr, return (ret); if (target_tv_addr) { - if (copy_from_user_timeval(&tv, target_tv_addr)) + if (target_to_host_timeval(&tv, target_tv_addr)) return (-TARGET_EFAULT); tv_ptr = &tv; } else { @@ -1269,7 +1323,7 @@ do_freebsd_select(int n, abi_ulong rfd_addr, abi_ulong wfd_addr, return (-TARGET_EFAULT); if (target_tv_addr && - fbsd_copy_to_user_timeval(&tv, target_tv_addr)) + host_to_target_timeval(&tv, target_tv_addr)) return (-TARGET_EFAULT); } @@ -2483,234 +2537,1659 @@ do_thr_set_name(long tid, char *name) #endif /* CONFIG_USE_NPTL */ static int -do_umtx_lock(abi_ulong umtx_addr, uint32_t id) +tcmpset_al(abi_ulong *addr, abi_ulong a, abi_ulong b) { - int ret = 0; + abi_ulong current = tswapal(a); + abi_ulong new = tswapal(b); + +#ifdef TARGET_ABI32 + return (atomic_cmpset_acq_32(addr, current, new)); +#else + return (atomic_cmpset_acq_64(addr, current, new)); +#endif +} + +static int +tcmpset_32(uint32_t *addr, uint32_t a, uint32_t b) +{ + uint32_t current = tswap32(a); + uint32_t new = tswap32(b); + + return (atomic_cmpset_acq_32(addr, current, new)); +} + +static int +do_lock_umtx(abi_ulong target_addr, abi_long id, struct timespec *timeout) +{ + abi_long owner; + int ret; + /* + * XXX Note that memory at umtx_addr can change and so we need to be + * careful and check for faults. + */ for (;;) { - ret = get_errno(_umtx_op(g2h(umtx_addr + - offsetof(struct target_umtx, u_owner)), - UMTX_OP_MUTEX_WAIT, UMTX_UNOWNED, 0, 0)); + struct target_umtx *target_umtx; + + if (!lock_user_struct(VERIFY_WRITE, target_umtx, target_addr, 0)) + return (-TARGET_EFAULT); + + /* Check the simple uncontested case. */ + if (tcmpset_al(&target_umtx->u_owner, + TARGET_UMTX_UNOWNED, id)) { + unlock_user_struct(target_umtx, target_addr, 1); + return (0); + } + + /* Check to see if the lock is contested but free. */ + __get_user(owner, &target_umtx->u_owner); + + if (TARGET_UMTX_CONTESTED == owner) { + if (tcmpset_al(&target_umtx->u_owner, + TARGET_UMTX_CONTESTED, + id | TARGET_UMTX_CONTESTED)) { + unlock_user_struct(target_umtx, target_addr, 1); + return (0); + } + + /* We failed because it changed on us, restart. */ + unlock_user_struct(target_umtx, target_addr, 1); + continue; + } + + /* Set the contested bit and sleep. */ + do { + __get_user(owner, &target_umtx->u_owner); + if (owner & TARGET_UMTX_CONTESTED) + break; + } while (!tcmpset_al(&target_umtx->u_owner, owner, + owner | TARGET_UMTX_CONTESTED)); + + __get_user(owner, &target_umtx->u_owner); + unlock_user_struct(target_umtx, target_addr, 1); + + /* Byte swap, if needed, to match what is stored in user mem. */ + owner = tswapal(owner); +#ifdef TARGET_ABI32 + ret = get_errno(_umtx_op(target_umtx, UMTX_OP_WAIT_UINT, owner, + NULL, timeout)); +#else + ret = get_errno(_umtx_op(target_umtx, UMTX_OP_WAIT, owner, + NULL, timeout)); +#endif if (ret) return (ret); - if (atomic_cmpset_acq_32(g2h(umtx_addr + - offsetof(struct target_umtx, u_owner)), - UMTX_UNOWNED, id)) - return (0); } } static int -do_umtx_unlock(abi_ulong umtx_addr, uint32 id) +do_unlock_umtx(abi_ulong target_addr, abi_ulong id) { - uint32_t owner; + abi_ulong owner; + struct target_umtx *target_umtx; - do { - if (get_user_u32(owner, umtx_addr + - offsetof(struct target_umtx, u_owner))) - return (-TARGET_EFAULT); - if (owner != id) - return (-TARGET_EPERM); - } while (!atomic_cmpset_rel_32(g2h(umtx_addr + - offsetof(struct target_umtx, u_owner)), owner, - UMUTEX_UNOWNED)); + if (!lock_user_struct(VERIFY_WRITE, target_umtx, target_addr, 0)) + return (-TARGET_EFAULT); + + __get_user(owner, &target_umtx->u_owner); + if ((owner & ~TARGET_UMTX_CONTESTED) != id) { + unlock_user_struct(target_umtx, target_addr, 1); + return (-TARGET_EPERM); + } + + /* Check the simple uncontested case. */ + if ((owner & ~TARGET_UMTX_CONTESTED) == 0) + if (tcmpset_al(&target_umtx->u_owner, owner, + TARGET_UMTX_UNOWNED)) { + unlock_user_struct(target_umtx, target_addr, 1); + return (0); + } + + /* This is a contested lock. Unlock it. */ + __put_user(TARGET_UMTX_UNOWNED, &target_umtx->u_owner); + unlock_user_struct(target_umtx, target_addr, 1); + + /* Wake up all those contesting it. */ + _umtx_op(target_umtx, UMTX_OP_WAKE, 0, 0, 0); return (0); } - -/* do_syscall() should always have a single exit point at the end so - that actions, such as logging of syscall results, can be performed. - All errnos that do_syscall() returns must be -TARGET_. */ -abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, - abi_long arg2, abi_long arg3, abi_long arg4, - abi_long arg5, abi_long arg6, abi_long arg7, - abi_long arg8) +static int +do_lock_umutex(abi_ulong target_addr, uint32_t id, struct timespec *ts, + int mode) { - abi_long ret; - void *p; - struct stat st; + uint32_t owner, flags; + int ret; -#ifdef DEBUG - gemu_log("freebsd syscall %d\n", num); -#endif - if(do_strace) - print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); - - switch(num) { - case TARGET_FREEBSD_NR_exit: -#ifdef TARGET_GPROF - _mcleanup(); -#endif - gdb_exit(cpu_env, arg1); - /* XXX: should free thread stack and CPU env */ - _exit(arg1); - ret = 0; /* avoid warning */ - break; - case TARGET_FREEBSD_NR_read: - if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) - goto efault; - ret = get_errno(read(arg1, p, arg3)); - unlock_user(p, arg2, ret); - break; + for (;;) { + struct target_umutex *target_umutex; - case TARGET_FREEBSD_NR_readv: - { - int count = arg3; - struct iovec *vec; + if (!lock_user_struct(VERIFY_WRITE, target_umutex, + target_addr, 0)) + return (-TARGET_EFAULT); - vec = alloca(count * sizeof(struct iovec)); - if (lock_iovec(VERIFY_WRITE, vec, arg2, count, 0) < 0) - goto efault; - ret = get_errno(readv(arg1, vec, count)); - unlock_iovec(vec, arg2, count, 1); - } - break; + __get_user(owner, &target_umutex->m_owner); - case TARGET_FREEBSD_NR_pread: - if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) - goto efault; - ret = get_errno(pread(arg1, p, arg3, target_offset64(arg4, arg5))); - unlock_user(p, arg2, ret); - break; + if (TARGET_UMUTEX_WAIT == mode) { + if (TARGET_UMUTEX_UNOWNED == owner || + TARGET_UMUTEX_CONTESTED == owner) + unlock_user_struct(target_umutex, + target_addr, 1); + return (0); + } else { + if (tcmpset_32(&target_umutex->m_owner, + TARGET_UMUTEX_UNOWNED, id)) { + /* The acquired succeeded. */ + unlock_user_struct(target_umutex, + target_addr, 1); + return (0); + } - case TARGET_FREEBSD_NR_preadv: - { - int count = arg3; - struct iovec *vec; + /* + * If no one owns it but it is contested try to acquire + * it. + */ + if (TARGET_UMUTEX_CONTESTED == owner) { + if (tcmpset_32(&target_umutex->m_owner, + TARGET_UMUTEX_CONTESTED, + id | TARGET_UMUTEX_CONTESTED)) { - vec = alloca(count * sizeof(struct iovec)); - if (lock_iovec(VERIFY_WRITE, vec, arg2, count, 0) < 0) - goto efault; - ret = get_errno(preadv(arg1, vec, count, - target_offset64(arg4, arg5))); - unlock_iovec(vec, arg2, count, 1); - } - break; + unlock_user_struct(target_umutex, + target_addr, 1); + return (0); + } - case TARGET_FREEBSD_NR_write: - if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) - goto efault; - ret = get_errno(write(arg1, p, arg3)); - unlock_user(p, arg2, 0); - break; + /* The lock changed so restart. */ + unlock_user_struct(target_umutex, + target_addr, 1); + continue; + } + } - case TARGET_FREEBSD_NR_writev: - { - int count = arg3; - struct iovec *vec; + __get_user(flags, &target_umutex->m_flags); + flags = tswap32(flags); + if ((flags & TARGET_UMUTEX_ERROR_CHECK) != 0 && + (owner & ~TARGET_UMUTEX_CONTESTED) == id) { + unlock_user_struct(target_umutex, target_addr, 1); + return (-TARGET_EDEADLK); + } - vec = alloca(count * sizeof(struct iovec)); - if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0) - goto efault; - ret = get_errno(writev(arg1, vec, count)); - unlock_iovec(vec, arg2, count, 0); - } - break; + if (TARGET_UMUTEX_TRY == mode) { + unlock_user_struct(target_umutex, target_addr, 1); + return (-TARGET_EBUSY); + } - case TARGET_FREEBSD_NR_pwrite: - if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) - goto efault; - ret = get_errno(pwrite(arg1, p, arg3, target_offset64(arg4, arg5))); - unlock_user(p, arg2, 0); - break; + /* Set the contested bit and sleep. */ + if (!tcmpset_32(&target_umutex->m_owner, owner, + owner | TARGET_UMUTEX_CONTESTED)) { + unlock_user_struct(target_umutex, target_addr, 1); + continue; + } - case TARGET_FREEBSD_NR_pwritev: - { - int count = arg3; - struct iovec *vec; + owner = owner | TARGET_UMUTEX_CONTESTED; + unlock_user_struct(target_umutex, target_addr, 1); - vec = alloca(count * sizeof(struct iovec)); - if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0) - goto efault; - ret = get_errno(pwritev(arg1, vec, count, - target_offset64(arg4, arg5))); - unlock_iovec(vec, arg2, count, 0); + /* Byte swap, if needed, to match what is stored in user mem. */ + owner = tswap32(owner); + ret = get_errno(_umtx_op(target_umutex, UMTX_OP_WAIT_UINT, owner, + 0, ts)); + if (ret) + return (ret); } - break; - case TARGET_FREEBSD_NR_open: - if (!(p = lock_user_string(arg1))) - goto efault; - ret = get_errno(open(path(p), - target_to_host_bitmask(arg2, fcntl_flags_tbl), - arg3)); - unlock_user(p, arg1, 0); - break; + return (0); +} - case TARGET_FREEBSD_NR_openat: - if (!(p = lock_user_string(arg2))) - goto efault; - ret = get_errno(openat(arg1, path(p), - target_to_host_bitmask(arg3, fcntl_flags_tbl), - arg4)); - unlock_user(p, arg2, 0); - break; +static int +do_unlock_umutex(abi_ulong target_addr, uint32_t id) +{ + struct target_umutex *target_umutex; + uint32_t owner; - case TARGET_FREEBSD_NR_close: - ret = get_errno(close(arg1)); - break; - case TARGET_FREEBSD_NR_closefrom: - ret = 0; - closefrom(arg1); - break; + if (!lock_user_struct(VERIFY_WRITE, target_umutex, target_addr, 0)) + return (-TARGET_EFAULT); -#ifdef TARGET_FREEBSD_NR_creat - case TARGET_FREEBSD_NR_creat: - if (!(p = lock_user_string(arg1))) - goto efault; - ret = get_errno(creat(p, arg2)); - unlock_user(p, arg1, 0); - break; -#endif + /* Make sure we own this mutex. */ + __get_user(owner, &target_umutex->m_owner); + if ((owner & ~TARGET_UMUTEX_CONTESTED) != id) { + unlock_user_struct(target_umutex, target_addr, 1); + return (-TARGET_EPERM); + } - case TARGET_FREEBSD_NR_mmap: - ret = get_errno(target_mmap(arg1, arg2, arg3, - target_to_host_bitmask(arg4, mmap_flags_tbl), - arg5, - arg6)); - break; + if ((owner & TARGET_UMUTEX_CONTESTED) == 0) + if (tcmpset_32(&target_umutex->m_owner, owner, + TARGET_UMTX_UNOWNED)) { + unlock_user_struct(target_umutex, target_addr, 1); + return (0); + } - case TARGET_FREEBSD_NR_munmap: - ret = get_errno(target_munmap(arg1, arg2)); - break; + /* This is a contested lock. Unlock it. */ + __put_user(TARGET_UMUTEX_UNOWNED, &target_umutex->m_owner); + unlock_user_struct(target_umutex, target_addr, 1); - case TARGET_FREEBSD_NR_mprotect: - ret = get_errno(target_mprotect(arg1, arg2, arg3)); - break; + /* And wake up all those contesting it. */ + return ( _umtx_op(g2h(target_addr), UMTX_OP_WAKE, 0, 0, 0)); +} - case TARGET_FREEBSD_NR_msync: - ret = get_errno(msync(g2h(arg1), arg2, arg3)); - break; +/* + * _cv_mutex is keeps other threads from doing a signal or broadcast until + * the thread is actually asleep and ready. This is a global mutex for all + * condition vars so I am sure performance may be a problem if there are lots + * of CVs. + */ +static struct umutex _cv_mutex = {0,0,{0,0},{0,0,0,0}}; - case TARGET_FREEBSD_NR_mlock: - ret = get_errno(mlock(g2h(arg1), arg2)); - break; - case TARGET_FREEBSD_NR_munlock: - ret = get_errno(munlock(g2h(arg1), arg2)); - break; +/* + * wflags CVWAIT_CHECK_UNPARKING, CVWAIT_ABSTIME, CVWAIT_CLOCKID + */ +static int +do_cv_wait(abi_ulong target_ucond_addr, abi_ulong target_umtx_addr, + struct timespec *ts, int wflags) +{ + long tid; + int ret; - case TARGET_FREEBSD_NR_mlockall: - ret = get_errno(mlockall(arg1)); - break; + if (! access_ok(VERIFY_WRITE, target_ucond_addr, + sizeof(struct target_ucond))) { - case TARGET_FREEBSD_NR_munlockall: - ret = get_errno(munlockall()); - break; + return (-TARGET_EFAULT); + } - case TARGET_FREEBSD_NR_madvise: - /* - * A straight passthrough may not be safe because qemu sometimes - * turns private file-backed mapping into anonymous mappings. This - * will break MADV_DONTNEED. This is a hint, so ignoring and returing - * success is ok. - */ - ret = get_errno(0); - break; + /* Check the clock ID if needed. */ + if ((wflags & TARGET_CVWAIT_CLOCKID) != 0) { + struct target_ucond *target_ucond; + uint32_t clockid; - case TARGET_FREEBSD_NR_break: + if (!lock_user_struct(VERIFY_WRITE, target_ucond, + target_ucond_addr, 0)) + return (-TARGET_EFAULT); + __get_user(clockid, &target_ucond->c_clockid); + unlock_user_struct(target_ucond, target_ucond_addr, 1); + if (clockid < CLOCK_REALTIME || + clockid >= CLOCK_THREAD_CPUTIME_ID) { + /* Only HW clock id will work. */ + return (-TARGET_EINVAL); + } + } + + thr_self(&tid); + + /* Lock the _cv_mutex so we can safely unlock the user mutex */ + _umtx_op(&_cv_mutex, UMTX_OP_MUTEX_LOCK, 0, NULL, NULL); + + /* unlock the user mutex */ + ret = do_unlock_umutex(target_umtx_addr, tid); + if (ret) { + _umtx_op(&_cv_mutex, UMTX_OP_MUTEX_UNLOCK, 0, NULL, NULL); + return (ret); + } + + /* UMTX_OP_CV_WAIT unlocks _cv_mutex */ + ret = get_errno(_umtx_op(g2h(target_ucond_addr), UMTX_OP_CV_WAIT, + wflags, &_cv_mutex, ts)); + + return (ret); +} + +static int +do_cv_signal(abi_ulong target_ucond_addr) +{ + int ret; + + if (! access_ok(VERIFY_WRITE, target_ucond_addr, + sizeof(struct target_ucond))) + return (-TARGET_EFAULT); + + /* Lock the _cv_mutex to prevent a race in do_cv_wait(). */ + _umtx_op(&_cv_mutex, UMTX_OP_MUTEX_LOCK, 0, NULL, NULL); + ret = get_errno(_umtx_op(g2h(target_ucond_addr), UMTX_OP_CV_SIGNAL, 0, + NULL, NULL)); + _umtx_op(&_cv_mutex, UMTX_OP_MUTEX_UNLOCK, 0, NULL, NULL); + + return (ret); +} + +static int +do_cv_broadcast(abi_ulong target_ucond_addr) +{ + int ret; + + if (! access_ok(VERIFY_WRITE, target_ucond_addr, + sizeof(struct target_ucond))) + return (-TARGET_EFAULT); + + /* Lock the _cv_mutex to prevent a race in do_cv_wait(). */ + _umtx_op(&_cv_mutex, UMTX_OP_MUTEX_LOCK, 0, NULL, NULL); + ret = get_errno(_umtx_op(g2h(target_ucond_addr), UMTX_OP_CV_BROADCAST, + 0, NULL, NULL)); + _umtx_op(&_cv_mutex, UMTX_OP_MUTEX_UNLOCK, 0, NULL, NULL); + + return (ret); +} + +static int +do_umtx_op_wait(abi_ulong target_addr, abi_ulong id, struct timespec *ts) +{ + + /* We want to check the user memory but not lock it. We might sleep. */ + if (! access_ok(VERIFY_READ, target_addr, sizeof(abi_ulong))) + return (-TARGET_EFAULT); + + /* id has already been byte swapped to match what may be in user mem. */ +#ifdef TARGET_ABI32 + return (get_errno(_umtx_op(g2h(target_addr), UMTX_OP_WAIT_UINT, id, NULL, + ts))); +#else + return (get_errno(_umtx_op(g2h(target_addr), UMTX_OP_WAIT, id, NULL, + ts))); +#endif +} + +static int +do_umtx_op_wake(abi_ulong target_addr, abi_ulong n_wake) +{ + + return (get_errno(_umtx_op(g2h(target_addr), UMTX_OP_WAKE, n_wake, NULL, + 0))); +} + +static int +do_rw_rdlock(abi_ulong target_addr, long fflag, struct timespec *ts) +{ + struct target_urwlock *target_urwlock; + uint32_t flags, wrflags; + uint32_t state; + uint32_t blocked_readers; + int ret; + + if (!lock_user_struct(VERIFY_WRITE, target_urwlock, target_addr, 0)) + return (-TARGET_EFAULT); + + __get_user(flags, &target_urwlock->rw_flags); + wrflags = TARGET_URWLOCK_WRITE_OWNER; + if (!(fflag & TARGET_URWLOCK_PREFER_READER) && + !(flags & TARGET_URWLOCK_PREFER_READER)) + wrflags |= TARGET_URWLOCK_WRITE_WAITERS; + + for (;;) { + __get_user(state, &target_urwlock->rw_state); + /* try to lock it */ + while (!(state & wrflags)) { + if (TARGET_URWLOCK_READER_COUNT(state) == + TARGET_URWLOCK_MAX_READERS) { + unlock_user_struct(target_urwlock, + target_addr, 1); + return (-TARGET_EAGAIN); + } + if (tcmpset_32(&target_urwlock->rw_state, state, + (state + 1))) { + /* The acquired succeeded. */ + unlock_user_struct(target_urwlock, + target_addr, 1); + return (0); + } + __get_user(state, &target_urwlock->rw_state); + } + + /* set read contention bit */ + if (! tcmpset_32(&target_urwlock->rw_state, state, + state | TARGET_URWLOCK_READ_WAITERS)) { + /* The state has changed. Start over. */ + continue; + } + + /* contention bit is set, increase read waiter count */ + __get_user(blocked_readers, &target_urwlock->rw_blocked_readers); + while (! tcmpset_32(&target_urwlock->rw_blocked_readers, + blocked_readers, blocked_readers + 1)) { + __get_user(blocked_readers, + &target_urwlock->rw_blocked_readers); + } + + while (state & wrflags) { + /* sleep/wait */ + unlock_user_struct(target_urwlock, target_addr, 1); + ret = get_errno(_umtx_op( + &target_urwlock->rw_blocked_readers, + UMTX_OP_WAIT_UINT, blocked_readers, 0, ts)); + if (ret) + return (ret); + if (!lock_user_struct(VERIFY_WRITE, target_urwlock, + target_addr, 0)) + return (-TARGET_EFAULT); + __get_user(state, &target_urwlock->rw_state); + } + + /* decrease read waiter count */ + __get_user(blocked_readers, &target_urwlock->rw_blocked_readers); + while (! tcmpset_32(&target_urwlock->rw_blocked_readers, + blocked_readers, (blocked_readers - 1))) { + __get_user(blocked_readers, + &target_urwlock->rw_blocked_readers); + } + if (1 == blocked_readers) { + /* clear read contention bit */ + __get_user(state, &target_urwlock->rw_state); + while(! tcmpset_32(&target_urwlock->rw_state, state, + state & ~TARGET_URWLOCK_READ_WAITERS)) { + __get_user(state, &target_urwlock->rw_state); + } + } + } +} + +static int +do_rw_wrlock(abi_ulong target_addr, long fflag, struct timespec *ts) +{ + struct target_urwlock *target_urwlock; + uint32_t blocked_readers, blocked_writers; + uint32_t state; + int ret; + + if (!lock_user_struct(VERIFY_WRITE, target_urwlock, target_addr, 0)) + return (-TARGET_EFAULT); + + blocked_readers = 0; + for (;;) { + __get_user(state, &target_urwlock->rw_state); + while (!(state & TARGET_URWLOCK_WRITE_OWNER) && + TARGET_URWLOCK_READER_COUNT(state) == 0) { + if (tcmpset_32(&target_urwlock->rw_state, state, + state | TARGET_URWLOCK_WRITE_OWNER)) { + unlock_user_struct(target_urwlock, + target_addr, 1); + return (0); + } + __get_user(state, &target_urwlock->rw_state); + } + + if (!(state & (TARGET_URWLOCK_WRITE_OWNER | + TARGET_URWLOCK_WRITE_WAITERS)) && + blocked_readers != 0) { + ret = get_errno(_umtx_op( + &target_urwlock->rw_blocked_readers, + UMTX_OP_WAKE, INT_MAX, NULL, NULL)); + return (ret); + } + + /* re-read the state */ + __get_user(state, &target_urwlock->rw_state); + + /* and set TARGET_URWLOCK_WRITE_WAITERS */ + while (((state & TARGET_URWLOCK_WRITE_OWNER) || + TARGET_URWLOCK_READER_COUNT(state) != 0) && + (state & TARGET_URWLOCK_WRITE_WAITERS) == 0) { + if (tcmpset_32(&target_urwlock->rw_state, state, + state | TARGET_URWLOCK_WRITE_WAITERS)) { + break; + } + __get_user(state, &target_urwlock->rw_state); + } + + /* contention bit is set, increase write waiter count */ + __get_user(blocked_writers, &target_urwlock->rw_blocked_writers); + while (! tcmpset_32(&target_urwlock->rw_blocked_writers, + blocked_writers, blocked_writers + 1)) { + __get_user(blocked_writers, + &target_urwlock->rw_blocked_writers); + } + + /* sleep */ + while ((state & TARGET_URWLOCK_WRITE_OWNER) || + (TARGET_URWLOCK_READER_COUNT(state) != 0)) { + unlock_user_struct(target_urwlock, target_addr, 1); + ret = get_errno(_umtx_op( + &target_urwlock->rw_blocked_writers, + UMTX_OP_WAIT_UINT, blocked_writers, 0, ts)); + if (ret) + return (ret); + if (!lock_user_struct(VERIFY_WRITE, target_urwlock, + target_addr, 0)) + return (-TARGET_EFAULT); + __get_user(state, &target_urwlock->rw_state); + } + + /* decrease the write waiter count */ + __get_user(blocked_writers, &target_urwlock->rw_blocked_writers); + while (! tcmpset_32(&target_urwlock->rw_blocked_writers, + blocked_writers, (blocked_writers - 1))) { + __get_user(blocked_writers, + &target_urwlock->rw_blocked_writers); + } + if (1 == blocked_writers) { + /* clear write contention bit */ + __get_user(state, &target_urwlock->rw_state); + while(! tcmpset_32(&target_urwlock->rw_state, state, + state & ~TARGET_URWLOCK_WRITE_WAITERS)) { + __get_user(state, &target_urwlock->rw_state); + } + __get_user(blocked_readers, + &target_urwlock->rw_blocked_readers); + } else + blocked_readers = 0; + } +} + +static int +do_rw_unlock(abi_ulong target_addr) +{ + struct target_urwlock *target_urwlock; + uint32_t flags, state, count; + void *q = NULL; + + if (!lock_user_struct(VERIFY_WRITE, target_urwlock, target_addr, 0)) + return (-TARGET_EFAULT); + + __get_user(flags, &target_urwlock->rw_flags); + __get_user(state, &target_urwlock->rw_state); + + if (state & TARGET_URWLOCK_WRITE_OWNER) { + for (;;) { + if (! tcmpset_32(&target_urwlock->rw_state, state, + state & ~TARGET_URWLOCK_WRITE_OWNER)) { + __get_user(state, &target_urwlock->rw_state); + if (!(state & TARGET_URWLOCK_WRITE_OWNER)) { + unlock_user_struct(target_urwlock, + target_addr, 1); + return (-TARGET_EPERM); + } + } else + break; + } + } else if (TARGET_URWLOCK_READER_COUNT(state) != 0) { + /* decrement reader count */ + for (;;) { + if (! tcmpset_32(&target_urwlock->rw_state, + state, (state - 1))) { + if (TARGET_URWLOCK_READER_COUNT(state) == 0) { + unlock_user_struct(target_urwlock, + target_addr, 1); + return (-TARGET_EPERM); + } + } else + break; + } + } else { + unlock_user_struct(target_urwlock, target_addr, 1); + return (-TARGET_EPERM); + } + + count = 0; + + if (! (flags & TARGET_URWLOCK_PREFER_READER)) { + if (state & TARGET_URWLOCK_WRITE_WAITERS) { + count = 1; + q = &target_urwlock->rw_blocked_writers; + } else if (state & TARGET_URWLOCK_READ_WAITERS) { + count = INT_MAX; + q = &target_urwlock->rw_blocked_readers; + } + } else { + if (state & TARGET_URWLOCK_READ_WAITERS) { + count = INT_MAX; + q = &target_urwlock->rw_blocked_readers; + } else if (state & TARGET_URWLOCK_WRITE_WAITERS) { + count = 1; + q = &target_urwlock->rw_blocked_writers; + } + } + + unlock_user_struct(target_urwlock, target_addr, 1); + if (q != NULL) + return (get_errno(_umtx_op(q, UMTX_OP_WAKE, count, NULL, NULL))); + else + return (0); +} + +static inline abi_long +target_to_host_statfs(struct statfs *host_statfs, abi_ulong target_addr) +{ + struct target_statfs *target_statfs; + + if (!lock_user_struct(VERIFY_READ, target_statfs, target_addr, 1)) + return (-TARGET_EFAULT); + __get_user(host_statfs->f_version, &target_statfs->f_version); + __get_user(host_statfs->f_type, &target_statfs->f_type); + __get_user(host_statfs->f_flags, &target_statfs->f_flags); + __get_user(host_statfs->f_bsize, &target_statfs->f_bsize); + __get_user(host_statfs->f_iosize, &target_statfs->f_iosize); + __get_user(host_statfs->f_blocks, &target_statfs->f_blocks); + __get_user(host_statfs->f_bfree, &target_statfs->f_bfree); + __get_user(host_statfs->f_bavail, &target_statfs->f_bavail); + __get_user(host_statfs->f_files, &target_statfs->f_files); + __get_user(host_statfs->f_ffree, &target_statfs->f_ffree); + __get_user(host_statfs->f_syncwrites, &target_statfs->f_syncwrites); + __get_user(host_statfs->f_asyncwrites, &target_statfs->f_asyncwrites); + __get_user(host_statfs->f_syncreads, &target_statfs->f_syncreads); + __get_user(host_statfs->f_asyncreads, &target_statfs->f_asyncreads); + /* uint64_t f_spare[10]; */ + __get_user(host_statfs->f_namemax, &target_statfs->f_namemax); + __get_user(host_statfs->f_owner, &target_statfs->f_owner); + __get_user(host_statfs->f_fsid.val[0], &target_statfs->f_fsid.val[0]); + __get_user(host_statfs->f_fsid.val[1], &target_statfs->f_fsid.val[1]); + /* char f_charspace[80]; */ + strncpy(host_statfs->f_fstypename, &target_statfs->f_fstypename[0], + TARGET_MFSNAMELEN); + strncpy(host_statfs->f_mntfromname, &target_statfs->f_mntfromname[0], + TARGET_MNAMELEN); + strncpy(host_statfs->f_mntonname, &target_statfs->f_mntonname[0], + TARGET_MNAMELEN); + unlock_user_struct(target_statfs, target_addr, 0); + return (0); +} + +static inline abi_long +host_to_target_statfs(abi_ulong target_addr, struct statfs *host_statfs) +{ + struct target_statfs *target_statfs; + + if (!lock_user_struct(VERIFY_WRITE, target_statfs, target_addr, 0)) + return (-TARGET_EFAULT); + __put_user(host_statfs->f_version, &target_statfs->f_version); + __put_user(host_statfs->f_type, &target_statfs->f_type); + __put_user(host_statfs->f_flags, &target_statfs->f_flags); + __put_user(host_statfs->f_bsize, &target_statfs->f_bsize); + __put_user(host_statfs->f_iosize, &target_statfs->f_iosize); + __put_user(host_statfs->f_blocks, &target_statfs->f_blocks); + __put_user(host_statfs->f_bfree, &target_statfs->f_bfree); + __put_user(host_statfs->f_bavail, &target_statfs->f_bavail); + __put_user(host_statfs->f_files, &target_statfs->f_files); + __put_user(host_statfs->f_ffree, &target_statfs->f_ffree); + __put_user(host_statfs->f_syncwrites, &target_statfs->f_syncwrites); + __put_user(host_statfs->f_asyncwrites, &target_statfs->f_asyncwrites); + __put_user(host_statfs->f_syncreads, &target_statfs->f_syncreads); + __put_user(host_statfs->f_asyncreads, &target_statfs->f_asyncreads); + /* uint64_t f_spare[10]; */ + __put_user(host_statfs->f_namemax, &target_statfs->f_namemax); + __put_user(host_statfs->f_owner, &target_statfs->f_owner); + __put_user(host_statfs->f_fsid.val[0], &target_statfs->f_fsid.val[0]); + __put_user(host_statfs->f_fsid.val[1], &target_statfs->f_fsid.val[1]); + /* char f_charspace[80]; */ + strncpy(&target_statfs->f_fstypename[0], host_statfs->f_fstypename, + TARGET_MFSNAMELEN); + strncpy(&target_statfs->f_mntfromname[0], host_statfs->f_mntfromname, + TARGET_MNAMELEN); + strncpy(&target_statfs->f_mntonname[0], host_statfs->f_mntonname, + TARGET_MNAMELEN); + unlock_user_struct(target_statfs, target_addr, 1); + return (0); +} + +static inline abi_long +target_to_host_fhandle(fhandle_t *host_fh, abi_ulong target_addr) +{ + target_fhandle_t *target_fh; + + if (!lock_user_struct(VERIFY_READ, target_fh, target_addr, 1)) + return (-TARGET_EFAULT); + __get_user(host_fh->fh_fsid.val[0], &target_fh->fh_fsid.val[0]); + __get_user(host_fh->fh_fsid.val[1], &target_fh->fh_fsid.val[0]); + + __get_user(host_fh->fh_fid.fid_len, &target_fh->fh_fid.fid_len); + /* u_short fid_data0; */ + memcpy(host_fh->fh_fid.fid_data, target_fh->fh_fid.fid_data, + TARGET_MAXFIDSZ); + unlock_user_struct(target_fh, target_addr, 0); + return (0); +} + +static inline abi_long +host_to_target_fhandle(abi_ulong target_addr, fhandle_t *host_fh) +{ + target_fhandle_t *target_fh; + + if (!lock_user_struct(VERIFY_WRITE, target_fh, target_addr, 0)) + return (-TARGET_EFAULT); + __put_user(host_fh->fh_fsid.val[0], &target_fh->fh_fsid.val[0]); + __put_user(host_fh->fh_fsid.val[1], &target_fh->fh_fsid.val[0]); + + __put_user(host_fh->fh_fid.fid_len, &target_fh->fh_fid.fid_len); + /* u_short fid_data0; */ + memcpy(target_fh->fh_fid.fid_data, host_fh->fh_fid.fid_data, + TARGET_MAXFIDSZ); + unlock_user_struct(target_fh, target_addr, 1); + return (0); +} + +static inline abi_long +target_to_host_sched_param(struct sched_param *host_sp, abi_ulong target_addr) +{ + struct target_sched_param *target_sp; + + if (!lock_user_struct(VERIFY_READ, target_sp, target_addr, 1)) + return (-TARGET_EFAULT); + __get_user(host_sp->sched_priority, &target_sp->sched_priority); + unlock_user_struct(target_sp, target_addr, 0); + return (0); +} + +static inline abi_long +host_to_target_sched_param(abi_ulong target_addr, struct sched_param *host_sp) +{ + struct target_sched_param *target_sp; + + if (!lock_user_struct(VERIFY_WRITE, target_sp, target_addr, 0)) + return (-TARGET_EFAULT); + __put_user(host_sp->sched_priority, &target_sp->sched_priority); + unlock_user_struct(target_sp, target_addr, 1); + return (0); +} + +static inline abi_long +do_sched_setparam(pid_t pid, abi_ulong target_sp_addr) +{ + int ret; + struct sched_param host_sp; + + ret = target_to_host_sched_param(&host_sp, target_sp_addr); + if (0 == ret) + ret = get_errno(sched_setparam(pid, &host_sp)); + + return (ret); +} + +static inline abi_long +do_sched_getparam(pid_t pid, abi_ulong target_sp_addr) +{ + int ret; + struct sched_param host_sp; + + ret = get_errno(sched_getparam(pid, &host_sp)); + if (0 == ret) + ret = host_to_target_sched_param(target_sp_addr, &host_sp); + + return (ret); +} + +static inline abi_long +do_sched_setscheduler(pid_t pid, int policy, abi_ulong target_sp_addr) +{ + int ret; + struct sched_param host_sp; + + ret = target_to_host_sched_param(&host_sp, target_sp_addr); + if (0 == ret) + ret = get_errno(sched_setscheduler(pid, policy, &host_sp)); + + return (ret); +} + +static inline abi_long +do_sched_rr_get_interval(pid_t pid, abi_ulong target_ts_addr) +{ + int ret; + struct timespec host_ts; + + ret = get_errno(sched_rr_get_interval(pid, &host_ts)); + if (0 == ret) + ret = host_to_target_timespec(target_ts_addr, &host_ts); + + return (ret); +} + +static inline abi_long +host_to_target_uuid(abi_ulong target_addr, struct uuid *host_uuid) +{ + struct target_uuid *target_uuid; + + if (!lock_user_struct(VERIFY_WRITE, target_uuid, target_addr, 0)) + return (-TARGET_EFAULT); + __put_user(host_uuid->time_low, &target_uuid->time_low); + __put_user(host_uuid->time_mid, &target_uuid->time_mid); + __put_user(host_uuid->time_hi_and_version, + &target_uuid->time_hi_and_version); + host_uuid->clock_seq_hi_and_reserved = + target_uuid->clock_seq_hi_and_reserved; + host_uuid->clock_seq_low = target_uuid->clock_seq_low; + memcpy(host_uuid->node, target_uuid->node, TARGET_UUID_NODE_LEN); + unlock_user_struct(target_uuid, target_addr, 1); + return (0); +} + +static inline abi_long +host_to_target_stat(abi_ulong target_addr, struct stat *host_st) +{ + struct target_freebsd_stat *target_st; + + if (!lock_user_struct(VERIFY_WRITE, target_st, target_addr, 0)) + return (-TARGET_EFAULT); + memset(target_st, 0, sizeof(*target_st)); + __put_user(host_st->st_dev, &target_st->st_dev); + __put_user(host_st->st_ino, &target_st->st_ino); + __put_user(host_st->st_mode, &target_st->st_mode); + __put_user(host_st->st_nlink, &target_st->st_nlink); + __put_user(host_st->st_uid, &target_st->st_uid); + __put_user(host_st->st_gid, &target_st->st_gid); + __put_user(host_st->st_rdev, &target_st->st_rdev); + __put_user(host_st->st_atim.tv_sec, &target_st->st_atim.tv_sec); + __put_user(host_st->st_atim.tv_nsec, &target_st->st_atim.tv_nsec); + __put_user(host_st->st_mtim.tv_sec, &target_st->st_mtim.tv_sec); + __put_user(host_st->st_mtim.tv_nsec, &target_st->st_mtim.tv_nsec); + __put_user(host_st->st_ctim.tv_sec, &target_st->st_ctim.tv_sec); + __put_user(host_st->st_ctim.tv_nsec, &target_st->st_ctim.tv_nsec); + __put_user(host_st->st_size, &target_st->st_size); + __put_user(host_st->st_blocks, &target_st->st_blocks); + __put_user(host_st->st_blksize, &target_st->st_blksize); + __put_user(host_st->st_flags, &target_st->st_flags); + __put_user(host_st->st_gen, &target_st->st_gen); + /* st_lspare not used */ + __put_user(host_st->st_birthtim.tv_sec, &target_st->st_birthtim.tv_sec); + __put_user(host_st->st_birthtim.tv_nsec, + &target_st->st_birthtim.tv_nsec); + unlock_user_struct(target_st, target_addr, 1); + + return (0); +} + +static inline abi_long +do_getfh(const char *path, abi_ulong target_addr) +{ + abi_long ret; + fhandle_t host_fh; + + ret = get_errno(getfh(path, &host_fh)); + if (ret) + return (ret); + + return (host_to_target_fhandle(target_addr, &host_fh)); +} + +static inline abi_long +do_lgetfh(const char *path, abi_ulong target_addr) +{ + abi_long ret; + fhandle_t host_fh; + + ret = get_errno(lgetfh(path, &host_fh)); + if (ret) + return (ret); + + return (host_to_target_fhandle(target_addr, &host_fh)); +} + +static inline abi_long +do_fhopen(abi_ulong target_addr, int flags) +{ + abi_long ret; + fhandle_t host_fh; + + ret = target_to_host_fhandle(&host_fh, target_addr); + if (ret) + return (ret); + + return (get_errno(fhopen(&host_fh, flags))); +} + +static inline abi_long +do_fhstat(abi_ulong target_fhp_addr, abi_ulong target_sb_addr) +{ + abi_long ret; + fhandle_t host_fh; + struct stat host_sb; + + ret = target_to_host_fhandle(&host_fh, target_fhp_addr); + if (ret) + return (ret); + + ret = get_errno(fhstat(&host_fh, &host_sb)); + if (ret) + return (ret); + + return (host_to_target_stat(target_sb_addr, &host_sb)); +} + +static inline abi_long +do_fhstatfs(abi_ulong target_fhp_addr, abi_ulong target_stfs_addr) +{ + abi_long ret; + fhandle_t host_fh; + struct statfs host_stfs; + + ret = target_to_host_fhandle(&host_fh, target_fhp_addr); + if (ret) + return (ret); + + ret = get_errno(fhstatfs(&host_fh, &host_stfs)); + if (ret) + return (ret); + + return (host_to_target_statfs(target_stfs_addr, &host_stfs)); +} + +static inline abi_long +do_statfs(const char *path, abi_ulong target_addr) +{ + abi_long ret; + struct statfs host_stfs; + + ret = get_errno(statfs(path, &host_stfs)); + if (ret) + return (ret); + + return (host_to_target_statfs(target_addr, &host_stfs)); +} + +static inline abi_long +do_fstatfs(int fd, abi_ulong target_addr) +{ + abi_long ret; + struct statfs host_stfs; + + ret = get_errno(fstatfs(fd, &host_stfs)); + if (ret) + return (ret); + + return (host_to_target_statfs(target_addr, &host_stfs)); +} + +static inline abi_long +do_getfsstat(abi_ulong target_addr, abi_long bufsize, int flags) +{ + abi_long ret; + struct statfs *host_stfs; + int count; + long host_bufsize; + + count = bufsize / sizeof(struct target_statfs); + + /* if user buffer is NULL then return number of mounted FS's */ + if (0 == target_addr || 0 == count) + return (get_errno(getfsstat(NULL, 0, flags))); + + /* XXX check count to be reasonable */ + host_bufsize = sizeof(struct statfs) * count; + host_stfs = alloca(host_bufsize); + if (! host_stfs) + return (-TARGET_EINVAL); + + ret = count = get_errno(getfsstat(host_stfs, host_bufsize, flags)); + if (ret < 0) + return (ret); + + while (count--) + if (host_to_target_statfs( + (target_addr + (count * sizeof(struct target_statfs))), + &host_stfs[count])) + return (-TARGET_EFAULT); + + return (ret); +} + +static abi_long +do_uuidgen(abi_ulong target_addr, int count) +{ + int i; + abi_long ret; + struct uuid *host_uuid; + + if (count < 1 || count > 2048) + return (-TARGET_EINVAL); + + host_uuid = (struct uuid *)g_malloc(count * sizeof(struct uuid)); + + if (NULL == host_uuid) + return (-TARGET_EINVAL); + + ret = get_errno(uuidgen(host_uuid, count)); + if (ret) + goto out; + for(i = 0; i < count; i++) { + ret = host_to_target_uuid(target_addr + + (abi_ulong)(sizeof(struct target_uuid) * i), &host_uuid[i]); + if (ret) + goto out; + } + +out: + g_free(host_uuid); + return (ret); +} + +static abi_long +do_adjtime(abi_ulong target_delta_addr, abi_ulong target_old_addr) +{ + abi_long ret; + struct timeval host_delta, host_old; + + ret = target_to_host_timeval(&host_delta, target_delta_addr); + if (ret) + goto out; + + if (target_old_addr) { + ret = get_errno(adjtime(&host_delta, &host_old)); + if (ret) + goto out; + ret = host_to_target_timeval(&host_old, target_old_addr); + } else + ret = get_errno(adjtime(&host_delta, NULL)); + +out: + return (ret); +} + +static abi_long +do_ntp_adjtime(abi_ulong target_tx_addr) +{ + abi_long ret; + struct timex host_tx; + + ret = target_to_host_timex(&host_tx, target_tx_addr); + if (ret) + goto out; + + ret = get_errno(ntp_adjtime(&host_tx)); + +out: + return (ret); +} + +static abi_long +do_ntp_gettime(abi_ulong target_ntv_addr) +{ + abi_long ret; + struct ntptimeval host_ntv; + + ret = get_errno(ntp_gettime(&host_ntv)); + if (ret) + goto out; + + ret = host_to_target_ntptimeval(target_ntv_addr, &host_ntv); +out: + return (ret); +} + +/* + * ioctl() + */ + +static const bitmask_transtbl iflag_tbl[] = { + { TARGET_IGNBRK, TARGET_IGNBRK, IGNBRK, IGNBRK }, + { TARGET_BRKINT, TARGET_BRKINT, BRKINT, BRKINT }, + { TARGET_IGNPAR, TARGET_IGNPAR, IGNPAR, IGNPAR }, + { TARGET_PARMRK, TARGET_PARMRK, PARMRK, PARMRK }, + { TARGET_INPCK, TARGET_INPCK, INPCK, INPCK }, + { TARGET_ISTRIP, TARGET_ISTRIP, ISTRIP, ISTRIP }, + { TARGET_INLCR, TARGET_INLCR, INLCR, INLCR }, + { TARGET_IGNCR, TARGET_IGNCR, IGNCR, IGNCR }, + { TARGET_ICRNL, TARGET_ICRNL, ICRNL, ICRNL }, + { TARGET_IXON, TARGET_IXON, IXON, IXON }, + { TARGET_IXOFF, TARGET_IXOFF, IXOFF, IXOFF }, +#ifdef IXANY + { TARGET_IXANY, TARGET_IXANY, IXANY, IXANY }, +#endif +#ifdef IMAXBEL + { TARGET_IMAXBEL, TARGET_IMAXBEL, IMAXBEL, IMAXBEL }, +#endif + { 0, 0, 0, 0 } +}; + +static const bitmask_transtbl oflag_tbl[] = { + { TARGET_OPOST, TARGET_OPOST, OPOST, OPOST }, +#ifdef ONLCR + { TARGET_ONLCR, TARGET_ONLCR, ONLCR, ONLCR }, +#endif +#ifdef TABDLY + { TARGET_TABDLY, TARGET_TAB0, TABDLY, TAB0 }, + { TARGET_TABDLY, TARGET_TAB3, TABDLY, TAB3 }, +#endif +#ifdef ONOEOT + { TARGET_ONOEOT, TARGET_ONOEOT, ONOEOT, ONOEOT }, +#endif +#ifdef OCRNL + { TARGET_OCRNL, TARGET_OCRNL, OCRNL, OCRNL }, +#endif +#ifdef ONOCR + { TARGET_ONOCR, TARGET_ONOCR, ONOCR, ONOCR }, +#endif +#ifdef ONLRET + { TARGET_ONLRET, TARGET_ONLRET, ONLRET, ONLRET }, +#endif + { 0, 0, 0, 0 } +}; + +static const bitmask_transtbl cflag_tbl[] = { +#ifdef CIGNORE + { TARGET_CIGNORE, TARGET_CIGNORE, CIGNORE, CIGNORE }, +#endif + { TARGET_CSIZE, TARGET_CS5, CSIZE, CS5 }, + { TARGET_CSIZE, TARGET_CS6, CSIZE, CS6 }, + { TARGET_CSIZE, TARGET_CS7, CSIZE, CS7 }, + { TARGET_CSIZE, TARGET_CS8, CSIZE, CS8 }, + { TARGET_CSTOPB, TARGET_CSTOPB, CSTOPB, CSTOPB }, + { TARGET_CREAD, TARGET_CREAD, CREAD, CREAD }, + { TARGET_PARENB, TARGET_PARENB, PARENB, PARENB }, + { TARGET_PARODD, TARGET_PARODD, PARODD, PARODD }, + { TARGET_HUPCL, TARGET_HUPCL, HUPCL, HUPCL }, + { TARGET_CLOCAL, TARGET_CLOCAL, CLOCAL, CLOCAL }, +#ifdef CCTS_OFLOW + { TARGET_CCTS_OFLOW, TARGET_CCTS_OFLOW, CCTS_OFLOW, CCTS_OFLOW }, +#endif +#ifdef CRTSCTS + { TARGET_CRTSCTS, TARGET_CRTSCTS, CRTSCTS, CRTSCTS }, +#endif +#ifdef CRTS_IFLOW + { TARGET_CRTS_IFLOW, TARGET_CRTS_IFLOW, CRTS_IFLOW, CRTS_IFLOW }, +#endif +#ifdef CDTS_IFLOW + { TARGET_CDTR_IFLOW, TARGET_CDTR_IFLOW, CDTR_IFLOW, CDTR_IFLOW }, +#endif +#ifdef CDSR_OFLOW + { TARGET_CDSR_OFLOW, TARGET_CDSR_OFLOW, CDSR_OFLOW, CDSR_OFLOW }, +#endif +#ifdef CCAR_OFLOW + { TARGET_CCAR_OFLOW, TARGET_CCAR_OFLOW, CCAR_OFLOW, CCAR_OFLOW }, +#endif + { 0, 0, 0, 0 } +}; + +static const bitmask_transtbl lflag_tbl[] = { +#ifdef ECHOKE + { TARGET_ECHOKE, TARGET_ECHOKE, ECHOKE, ECHOKE }, +#endif + { TARGET_ECHOE, TARGET_ECHOE, ECHOE, ECHOE }, + { TARGET_ECHOK, TARGET_ECHOK, ECHOK, ECHOK }, + { TARGET_ECHO, TARGET_ECHO, ECHO, ECHO }, + { TARGET_ECHONL, TARGET_ECHONL, ECHONL, ECHONL }, +#ifdef ECHOPRT + { TARGET_ECHOPRT, TARGET_ECHOPRT, ECHOPRT, ECHOPRT }, +#endif +#ifdef ECHOCTL + { TARGET_ECHOCTL, TARGET_ECHOCTL, ECHOCTL, ECHOCTL }, +#endif + { TARGET_ISIG, TARGET_ISIG, ISIG, ISIG }, + { TARGET_ICANON, TARGET_ICANON, ICANON, ICANON }, +#ifdef ALTWERASE + { TARGET_ALTWERASE, TARGET_ALTWERASE, ALTWERASE, ALTWERASE }, +#endif + { TARGET_IEXTEN, TARGET_IEXTEN, IEXTEN, IEXTEN }, + { TARGET_EXTPROC, TARGET_EXTPROC, EXTPROC, EXTPROC }, + { TARGET_TOSTOP, TARGET_TOSTOP, TOSTOP, TOSTOP }, +#ifdef FLUSHO + { TARGET_FLUSHO, TARGET_FLUSHO, FLUSHO, FLUSHO }, +#endif +#ifdef NOKERNINFO + { TARGET_NOKERNINFO, TARGET_NOKERNINFO, NOKERNINFO, NOKERNINFO }, +#endif +#ifdef PENDIN + { TARGET_PENDIN, TARGET_PENDIN, PENDIN, PENDIN }, +#endif + { TARGET_NOFLSH, TARGET_NOFLSH, NOFLSH, NOFLSH }, + { 0, 0, 0, 0 } +}; + +static void +target_to_host_termios(void *dst, const void *src) +{ + struct termios *host = dst; + const struct target_termios *target = src; + + host->c_iflag = + target_to_host_bitmask(tswap32(target->c_iflag), iflag_tbl); + host->c_oflag = + target_to_host_bitmask(tswap32(target->c_oflag), oflag_tbl); + host->c_cflag = + target_to_host_bitmask(tswap32(target->c_cflag), cflag_tbl); + host->c_lflag = + target_to_host_bitmask(tswap32(target->c_lflag), lflag_tbl); + + memset(host->c_cc, 0, sizeof(host->c_cc)); + host->c_cc[VEOF] = target->c_cc[TARGET_VEOF]; + host->c_cc[VEOL] = target->c_cc[TARGET_VEOL]; +#ifdef VEOL2 + host->c_cc[VEOL2] = target->c_cc[TARGET_VEOL2]; +#endif + host->c_cc[VERASE] = target->c_cc[TARGET_VERASE]; +#ifdef VWERASE + host->c_cc[VWERASE] = target->c_cc[TARGET_VWERASE]; +#endif + host->c_cc[VKILL] = target->c_cc[TARGET_VKILL]; +#ifdef VREPRINT + host->c_cc[VREPRINT] = target->c_cc[TARGET_VREPRINT]; +#endif +#ifdef VERASE2 + host->c_cc[VERASE2] = target->c_cc[TARGET_VERASE2]; +#endif + host->c_cc[VINTR] = target->c_cc[TARGET_VINTR]; + host->c_cc[VQUIT] = target->c_cc[TARGET_VQUIT]; + host->c_cc[VSUSP] = target->c_cc[TARGET_VSUSP]; +#ifdef VDSUSP + host->c_cc[VDSUSP] = target->c_cc[TARGET_VDSUSP]; +#endif + host->c_cc[VSTART] = target->c_cc[TARGET_VSTART]; + host->c_cc[VSTOP] = target->c_cc[TARGET_VSTOP]; +#ifdef VLNEXT + host->c_cc[VLNEXT] = target->c_cc[TARGET_VLNEXT]; +#endif +#ifdef VDISCARD + host->c_cc[VDISCARD] = target->c_cc[TARGET_VDISCARD]; +#endif + host->c_cc[VMIN] = target->c_cc[TARGET_VMIN]; + host->c_cc[VTIME] = target->c_cc[TARGET_VTIME]; +#ifdef VSTATUS + host->c_cc[VSTATUS] = target->c_cc[TARGET_VSTATUS]; +#endif + + host->c_ispeed = tswap32(target->c_ispeed); + host->c_ospeed = tswap32(target->c_ospeed); +} + +static void +host_to_target_termios(void *dst, const void *src) +{ + struct target_termios *target = dst; + const struct termios *host = src; + + target->c_iflag = + tswap32(host_to_target_bitmask(host->c_iflag, iflag_tbl)); + target->c_oflag = + tswap32(host_to_target_bitmask(host->c_oflag, oflag_tbl)); + target->c_cflag = + tswap32(host_to_target_bitmask(host->c_cflag, cflag_tbl)); + target->c_lflag = + tswap32(host_to_target_bitmask(host->c_lflag, lflag_tbl)); + + memset(target->c_cc, 0, sizeof(target->c_cc)); + target->c_cc[TARGET_VEOF] = host->c_cc[VEOF]; + target->c_cc[TARGET_VEOL] = host->c_cc[VEOL]; +#ifdef VEOL2 + target->c_cc[TARGET_VEOL2] = host->c_cc[VEOL2]; +#endif + target->c_cc[TARGET_VERASE] = host->c_cc[VERASE]; +#ifdef VWERASE + target->c_cc[TARGET_VWERASE] = host->c_cc[VWERASE]; +#endif + target->c_cc[TARGET_VKILL] = host->c_cc[VKILL]; +#ifdef VREPRINT + target->c_cc[TARGET_VREPRINT] = host->c_cc[VREPRINT]; +#endif +#ifdef VERASE2 + target->c_cc[TARGET_VERASE2] = host->c_cc[VERASE2]; +#endif + target->c_cc[TARGET_VINTR] = host->c_cc[VINTR]; + target->c_cc[TARGET_VQUIT] = host->c_cc[VQUIT]; + target->c_cc[TARGET_VSUSP] = host->c_cc[VSUSP]; +#ifdef VDSUSP + target->c_cc[TARGET_VDSUSP] = host->c_cc[VDSUSP]; +#endif + target->c_cc[TARGET_VSTART] = host->c_cc[VSTART]; + target->c_cc[TARGET_VSTOP] = host->c_cc[VSTOP]; +#ifdef VLNEXT + target->c_cc[TARGET_VLNEXT] = host->c_cc[VLNEXT]; +#endif +#ifdef VDISCARD + target->c_cc[TARGET_VDISCARD] = host->c_cc[VDISCARD]; +#endif + target->c_cc[TARGET_VMIN] = host->c_cc[VMIN]; + target->c_cc[TARGET_VTIME] = host->c_cc[VTIME]; +#ifdef VSTATUS + target->c_cc[TARGET_VSTATUS] = host->c_cc[VSTATUS]; +#endif + + target->c_ispeed = tswap32(host->c_ispeed); + target->c_ospeed = tswap32(host->c_ospeed); +} + +static const StructEntry struct_termios_def = { + .convert = { host_to_target_termios, target_to_host_termios }, + .size = { sizeof(struct target_termios), sizeof(struct termios) }, + .align = { __alignof__(struct target_termios), + __alignof__(struct termios) }, +}; + +/* kernel structure types definitions */ + +#define STRUCT(name, ...) STRUCT_ ## name, +#define STRUCT_SPECIAL(name) STRUCT_ ## name, +enum { +#ifdef __FreeBSD__ +#include "freebsd/syscall_types.h" +#else +#warning No syscall_types.h +#endif +}; +#undef STRUCT +#undef STRUCT_SPECIAL + +#define STRUCT(name, ...) \ + static const argtype struct_ ## name ## _def[] = { __VA_ARGS__, TYPE_NULL }; +#define STRUCT_SPECIAL(name) +#ifdef __FreeBSD__ +#include "freebsd/syscall_types.h" +#else +#warning No syscall_types.h +#endif +#undef STRUCT +#undef STRUCT_SPECIAL + +typedef struct IOCTLEntry IOCTLEntry; + +typedef abi_long do_ioctl_fn(const IOCTLEntry *ie, uint8_t *buf_temp, + int fd, abi_long cmd, abi_long arg); + +struct IOCTLEntry { + unsigned int target_cmd; + unsigned int host_cmd; + const char *name; + int access; + do_ioctl_fn *do_ioctl; + const argtype arg_type[5]; +}; + +#define MAX_STRUCT_SIZE 4096 + +static IOCTLEntry ioctl_entries[] = { +#define IOC_ 0x0000 +#define IOC_R 0x0001 +#define IOC_W 0x0002 +#define IOC_RW (IOC_R | IOC_W) +#define IOCTL(cmd, access, ...) \ + { TARGET_ ## cmd, cmd, #cmd, access, 0, { __VA_ARGS__ } }, +#define IOCTL_SPECIAL(cmd, access, dofn, ...) \ + { TARGET_ ## cmd, cmd, #cmd, access, dofn, { __VA_ARGS__ } }, +#ifdef __FreeBSD__ +#include "freebsd/ioctl.h" +#else +#warning No ioctl.h +#endif + { 0, 0 }, +}; + +static abi_long +do_ioctl(int fd, abi_long cmd, abi_long arg) +{ + const IOCTLEntry *ie; + const argtype *arg_type; + abi_long ret; + uint8_t buf_temp[MAX_STRUCT_SIZE]; + int target_size; + void *argptr; + + ie = ioctl_entries; + for(;;) { + if (0 == ie->target_cmd) { + gemu_log("Unsupported ioctl: cmd=0x%04lx\n", (long)cmd); + return (-TARGET_ENOSYS); + } + if (ie->target_cmd == cmd) + break; + ie++; + } + arg_type = ie->arg_type; +#if defined(DEBUG) + gemu_log("ioctl: cmd=0x%04lx (%s)\n", (long)cmd, ie->name); +#endif + if (ie->do_ioctl) { + return (ie->do_ioctl(ie, buf_temp, fd, cmd, arg)); + } + + switch(arg_type[0]) { + case TYPE_NULL: + /* no argument */ + ret = get_errno(ioctl(fd, ie->host_cmd)); + break; + + case TYPE_PTRVOID: + case TYPE_INT: + /* int argument */ + ret = get_errno(ioctl(fd, ie->host_cmd, arg)); + break; + + case TYPE_PTR: + arg_type++; + target_size = thunk_type_size(arg_type, 0); + switch(ie->access) { + case IOC_R: + ret = get_errno(ioctl(fd, ie->host_cmd, buf_temp)); + if (!is_error(ret)) { + argptr = lock_user(VERIFY_WRITE, arg, + target_size, 0); + if (!argptr) + return (-TARGET_EFAULT); + thunk_convert(argptr, buf_temp, arg_type, + THUNK_TARGET); + unlock_user(argptr, arg, target_size); + } + break; + + case IOC_W: + argptr = lock_user(VERIFY_READ, arg, target_size, 1); + if (!argptr) + return (-TARGET_EFAULT); + thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST); + unlock_user(argptr, arg, 0); + ret = get_errno(ioctl(fd, ie->host_cmd, buf_temp)); + + case IOC_RW: + default: + argptr = lock_user(VERIFY_READ, arg, target_size, 1); + if (!argptr) + return (-TARGET_EFAULT); + thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST); + unlock_user(argptr, arg, 0); + ret = get_errno(ioctl(fd, ie->host_cmd, buf_temp)); + if (!is_error(ret)) { + argptr = lock_user(VERIFY_WRITE, arg, + target_size, 0); + if (!argptr) + return (-TARGET_EFAULT); + thunk_convert(argptr, buf_temp, arg_type, + THUNK_TARGET); + unlock_user(argptr, arg, target_size); + } + break; + } + break; + + default: + gemu_log("Unsupported ioctl type: cmd=0x%04lx type=%d\n", + (long)cmd, arg_type[0]); + ret = -TARGET_ENOSYS; + break; + } + return (ret); +} + +/* do_syscall() should always have a single exit point at the end so + that actions, such as logging of syscall results, can be performed. + All errnos that do_syscall() returns must be -TARGET_. */ +abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, + abi_long arg5, abi_long arg6, abi_long arg7, + abi_long arg8) +{ + abi_long ret; + void *p; + +#ifdef DEBUG + gemu_log("freebsd syscall %d\n", num); +#endif + if(do_strace) + print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); + + switch(num) { + case TARGET_FREEBSD_NR_exit: +#ifdef TARGET_GPROF + _mcleanup(); +#endif + gdb_exit(cpu_env, arg1); + /* XXX: should free thread stack and CPU env */ + _exit(arg1); + ret = 0; /* avoid warning */ + break; + case TARGET_FREEBSD_NR_read: + if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) + goto efault; + ret = get_errno(read(arg1, p, arg3)); + unlock_user(p, arg2, ret); + break; + + case TARGET_FREEBSD_NR_readv: + { + int count = arg3; + struct iovec *vec; + + vec = alloca(count * sizeof(struct iovec)); + if (lock_iovec(VERIFY_WRITE, vec, arg2, count, 0) < 0) + goto efault; + ret = get_errno(readv(arg1, vec, count)); + unlock_iovec(vec, arg2, count, 1); + } + break; + + case TARGET_FREEBSD_NR_pread: + if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) + goto efault; + ret = get_errno(pread(arg1, p, arg3, target_offset64(arg4, arg5))); + unlock_user(p, arg2, ret); + break; + + case TARGET_FREEBSD_NR_preadv: + { + int count = arg3; + struct iovec *vec; + + vec = alloca(count * sizeof(struct iovec)); + if (lock_iovec(VERIFY_WRITE, vec, arg2, count, 0) < 0) + goto efault; + ret = get_errno(preadv(arg1, vec, count, + target_offset64(arg4, arg5))); + unlock_iovec(vec, arg2, count, 1); + } + break; + + case TARGET_FREEBSD_NR_write: + if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) + goto efault; + ret = get_errno(write(arg1, p, arg3)); + unlock_user(p, arg2, 0); + break; + + case TARGET_FREEBSD_NR_writev: + { + int count = arg3; + struct iovec *vec; + + vec = alloca(count * sizeof(struct iovec)); + if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0) + goto efault; + ret = get_errno(writev(arg1, vec, count)); + unlock_iovec(vec, arg2, count, 0); + } + break; + + case TARGET_FREEBSD_NR_pwrite: + if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) + goto efault; + ret = get_errno(pwrite(arg1, p, arg3, target_offset64(arg4, arg5))); + unlock_user(p, arg2, 0); + break; + + case TARGET_FREEBSD_NR_pwritev: + { + int count = arg3; + struct iovec *vec; + + vec = alloca(count * sizeof(struct iovec)); + if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0) + goto efault; + ret = get_errno(pwritev(arg1, vec, count, + target_offset64(arg4, arg5))); + unlock_iovec(vec, arg2, count, 0); + } + break; + + case TARGET_FREEBSD_NR_open: + if (!(p = lock_user_string(arg1))) + goto efault; + ret = get_errno(open(path(p), + target_to_host_bitmask(arg2, fcntl_flags_tbl), + arg3)); + unlock_user(p, arg1, 0); + break; + + case TARGET_FREEBSD_NR_openat: + if (!(p = lock_user_string(arg2))) + goto efault; + ret = get_errno(openat(arg1, path(p), + target_to_host_bitmask(arg3, fcntl_flags_tbl), + arg4)); + unlock_user(p, arg2, 0); + break; + + case TARGET_FREEBSD_NR_close: + ret = get_errno(close(arg1)); + break; + + case TARGET_FREEBSD_NR_closefrom: + ret = 0; + closefrom(arg1); + break; + + case TARGET_FREEBSD_NR_revoke: + if (!(p = lock_user_string(arg1))) + goto efault; + ret = get_errno(revoke(p)); + unlock_user(p, arg1, 0); + break; + +#ifdef TARGET_FREEBSD_NR_creat + case TARGET_FREEBSD_NR_creat: + if (!(p = lock_user_string(arg1))) + goto efault; + ret = get_errno(creat(p, arg2)); + unlock_user(p, arg1, 0); + break; +#endif + + case TARGET_FREEBSD_NR_mmap: + ret = get_errno(target_mmap(arg1, arg2, arg3, + target_to_host_bitmask(arg4, mmap_flags_tbl), + arg5, + arg6)); + break; + + case TARGET_FREEBSD_NR_munmap: + ret = get_errno(target_munmap(arg1, arg2)); + break; + + case TARGET_FREEBSD_NR_mprotect: + ret = get_errno(target_mprotect(arg1, arg2, arg3)); + break; + + case TARGET_FREEBSD_NR_msync: + ret = get_errno(msync(g2h(arg1), arg2, arg3)); + break; + + case TARGET_FREEBSD_NR_mlock: + ret = get_errno(mlock(g2h(arg1), arg2)); + break; + + case TARGET_FREEBSD_NR_munlock: + ret = get_errno(munlock(g2h(arg1), arg2)); + break; + + case TARGET_FREEBSD_NR_mlockall: + ret = get_errno(mlockall(arg1)); + break; + + case TARGET_FREEBSD_NR_munlockall: + ret = get_errno(munlockall()); + break; + + case TARGET_FREEBSD_NR_madvise: + /* + * A straight passthrough may not be safe because qemu sometimes + * turns private file-backed mapping into anonymous mappings. This + * will break MADV_DONTNEED. This is a hint, so ignoring and returing + * success is ok. + */ + ret = get_errno(0); + break; + + case TARGET_FREEBSD_NR_break: ret = do_obreak(arg1); break; #ifdef __FreeBSD__ @@ -2727,18 +4206,30 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, break; case TARGET_FREEBSD_NR_stat: - if (!(p = lock_user_string(arg1))) - goto efault; - ret = get_errno(stat(path(p), &st)); - unlock_user(p, arg1, 0); - goto do_stat; + { + struct stat st; + + if (!(p = lock_user_string(arg1))) + goto efault; + ret = get_errno(stat(path(p), &st)); + unlock_user(p, arg1, 0); + if (0 == ret) + ret = host_to_target_stat(arg2, &st); + } + break; case TARGET_FREEBSD_NR_lstat: - if (!(p = lock_user_string(arg1))) - goto efault; - ret = get_errno(lstat(path(p), &st)); - unlock_user(p, arg1, 0); - goto do_stat; + { + struct stat st; + + if (!(p = lock_user_string(arg1))) + goto efault; + ret = get_errno(lstat(path(p), &st)); + unlock_user(p, arg1, 0); + if (0 == ret) + ret = host_to_target_stat(arg2, &st); + } + break; case TARGET_FREEBSD_NR_nstat: case TARGET_FREEBSD_NR_nfstat: @@ -2747,42 +4238,11 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, break; case TARGET_FREEBSD_NR_fstat: - { - ret = get_errno(fstat(arg1, &st)); - -do_stat: - if (!is_error(ret)) { - struct target_freebsd_stat *target_st; - - if (!lock_user_struct(VERIFY_WRITE, target_st, arg2, 0)) - goto efault; - memset(target_st, 0, sizeof(*target_st)); - __put_user(st.st_dev, &target_st->st_dev); - __put_user(st.st_ino, &target_st->st_ino); - __put_user(st.st_mode, &target_st->st_mode); - __put_user(st.st_nlink, &target_st->st_nlink); - __put_user(st.st_uid, &target_st->st_uid); - __put_user(st.st_gid, &target_st->st_gid); - __put_user(st.st_rdev, &target_st->st_rdev); - __put_user(st.st_atim.tv_sec, &target_st->st_atim.tv_sec); - __put_user(st.st_atim.tv_nsec, &target_st->st_atim.tv_nsec); - __put_user(st.st_mtim.tv_sec, &target_st->st_mtim.tv_sec); - __put_user(st.st_mtim.tv_nsec, &target_st->st_mtim.tv_nsec); - __put_user(st.st_ctim.tv_sec, &target_st->st_ctim.tv_sec); - __put_user(st.st_ctim.tv_nsec, &target_st->st_ctim.tv_nsec); - __put_user(st.st_size, &target_st->st_size); - __put_user(st.st_blocks, &target_st->st_blocks); - __put_user(st.st_blksize, &target_st->st_blksize); - __put_user(st.st_flags, &target_st->st_flags); - __put_user(st.st_gen, &target_st->st_gen); - /* st_lspare not used */ - __put_user(st.st_birthtim.tv_sec, - &target_st->st_birthtim.tv_sec); - __put_user(st.st_birthtim.tv_nsec, - &target_st->st_birthtim.tv_nsec); - unlock_user_struct(target_st, arg2, 1); - } - + { + struct stat st; + ret = get_errno(fstat(arg1, &st)); + if (! ret) + ret = host_to_target_stat(arg2, &st); } break; @@ -2845,7 +4305,7 @@ do_stat: } ret = get_errno(gettimeofday(&tv, arg2 != 0 ? &tz : NULL)); if (!is_error(ret)) { - if (fbsd_copy_to_user_timeval(&tv, arg1)) + if (host_to_target_timeval(&tv, arg1)) goto efault; } } @@ -2864,7 +4324,7 @@ do_stat: __get_user(tz.tz_dsttime, &target_tz->tz_dsttime); unlock_user_struct(target_tz, arg2, 1); } - if (copy_from_user_timeval(&tv, arg1)) + if (target_to_host_timeval(&tv, arg1)) goto efault; ret = get_errno(settimeofday(&tv, arg2 != 0 ? & tz : NULL)); } @@ -3155,8 +4615,8 @@ do_stat: if (arg2) { pvalue = &value; - if (copy_from_user_timeval(&pvalue->it_interval, - arg2) || copy_from_user_timeval( + if (target_to_host_timeval(&pvalue->it_interval, + arg2) || target_to_host_timeval( &pvalue->it_value, arg2 + sizeof(struct target_timeval))) goto efault; @@ -3165,8 +4625,8 @@ do_stat: } ret = get_errno(setitimer(arg1, pvalue, &ovalue)); if (!is_error(ret) && arg3) { - if (fbsd_copy_to_user_timeval(&ovalue.it_interval, arg3) - || fbsd_copy_to_user_timeval(&ovalue.it_value, + if (host_to_target_timeval(&ovalue.it_interval, arg3) + || host_to_target_timeval(&ovalue.it_value, arg3 + sizeof(struct target_timeval))) goto efault; } @@ -3179,8 +4639,8 @@ do_stat: ret = get_errno(getitimer(arg1, &value)); if (!is_error(ret) && arg2) { - if (fbsd_copy_to_user_timeval(&value.it_interval, arg2) - || fbsd_copy_to_user_timeval(&value.it_value, + if (host_to_target_timeval(&value.it_interval, arg2) + || host_to_target_timeval(&value.it_value, arg2 + sizeof(struct target_timeval))) goto efault; } @@ -3192,8 +4652,8 @@ do_stat: struct timeval *tvp, tv[2]; if (arg2) { - if (copy_from_user_timeval(&tv[0], arg2) - || copy_from_user_timeval(&tv[1], + if (target_to_host_timeval(&tv[0], arg2) + || target_to_host_timeval(&tv[1], arg2 + sizeof(struct target_timeval))) goto efault; @@ -3213,8 +4673,8 @@ do_stat: struct timeval *tvp, tv[2]; if (arg2) { - if (copy_from_user_timeval(&tv[0], arg2) - || copy_from_user_timeval(&tv[1], + if (target_to_host_timeval(&tv[0], arg2) + || target_to_host_timeval(&tv[1], arg2 + sizeof(struct target_timeval))) goto efault; @@ -3234,8 +4694,8 @@ do_stat: struct timeval *tvp, tv[2]; if (arg2) { - if (copy_from_user_timeval(&tv[0], arg2) - || copy_from_user_timeval(&tv[1], + if (target_to_host_timeval(&tv[0], arg2) + || target_to_host_timeval(&tv[1], arg2 + sizeof(struct target_timeval))) goto efault; tvp = tv; @@ -3251,8 +4711,8 @@ do_stat: struct timeval *tvp, tv[2]; if (arg3) { - if (copy_from_user_timeval(&tv[0], arg3) - || copy_from_user_timeval(&tv[1], + if (target_to_host_timeval(&tv[0], arg3) + || target_to_host_timeval(&tv[1], arg3 + sizeof(struct target_timeval))) goto efault; tvp = tv; @@ -4024,6 +5484,10 @@ do_stat: ret = do_socketpair(arg1, arg2, arg3, arg4); break; + case TARGET_FREEBSD_NR_shutdown: + ret = get_errno(shutdown(arg1, arg2)); + break; + case TARGET_FREEBSD_NR_getpriority: /* * Note that negative values are valid for getpriority, so we must @@ -4165,8 +5629,31 @@ do_stat: break; case TARGET_FREEBSD_NR_getresuid: + { + uid_t ruid, euid, suid; + + ret = get_errno(getresuid(&ruid, &euid, &suid)); + if (put_user_s32(ruid, arg1)) + goto efault; + if (put_user_s32(euid, arg2)) + goto efault; + if (put_user_s32(suid, arg3)) + goto efault; + } + break; + case TARGET_FREEBSD_NR_getresgid: - ret = unimplemented(num); + { + gid_t rgid, egid, sgid; + + ret = get_errno(getresgid(&rgid, &egid, &sgid)); + if (put_user_s32(rgid, arg1)) + goto efault; + if (put_user_s32(egid, arg2)) + goto efault; + if (put_user_s32(sgid, arg3)) + goto efault; + } break; case TARGET_FREEBSD_NR_setsid: @@ -4679,7 +6166,7 @@ do_stat: long tid; thr_self(&tid); - ret = do_umtx_lock(arg1, tswap32(tid)); + ret = do_lock_umtx(arg1, tid, NULL); } break; @@ -4688,72 +6175,238 @@ do_stat: long tid; thr_self(&tid); - ret = do_umtx_unlock(arg1, tswap32(tid)); + ret = do_unlock_umtx(arg1, tid); } break; case TARGET_FREEBSD_NR__umtx_op: { struct timespec ts; - void *object = NULL; - int operation; - void *addr = NULL; - void *addr2 = NULL; - + long tid; /* int _umtx_op(void *obj, int op, u_long val, - * void *uaddr, void *uaddr2); */ + * void *uaddr, void *target_ts); */ abi_ulong obj = arg1; int op = (int)arg2; u_long val = arg3; - /* abi_ulong uaddr = arg4; */ - abi_ulong uaddr2 = arg5; + abi_ulong uaddr = arg4; + abi_ulong target_ts = arg5; switch(op) { case TARGET_UMTX_OP_LOCK: - ret = do_umtx_lock(obj, tswap32((uint32_t)val)); + thr_self(&tid); + if (target_ts) { + if (target_to_host_timespec(&ts, target_ts)) + goto efault; + ret = do_lock_umtx(obj, tid, &ts); + } else + ret = do_lock_umtx(obj, tid, NULL); break; case TARGET_UMTX_OP_UNLOCK: - ret = do_umtx_unlock(obj, tswap32((uint32_t)val)); + thr_self(&tid); + ret = do_unlock_umtx(obj, tid); break; case TARGET_UMTX_OP_WAIT: - if (uaddr2) { - if (target_to_host_timespec(&ts, uaddr2)) + /* args: obj *, val, ts * */ + if (target_ts) { + if (target_to_host_timespec(&ts, target_ts)) goto efault; - addr2 = (void *)&ts; - } - ret = get_errno(_umtx_op(g2h(obj), UMTX_OP_WAIT, - tswap32(val), addr, addr2)); - break; + ret = do_umtx_op_wait(obj, tswapal(val), &ts); + } else + ret = do_umtx_op_wait(obj, tswapal(val), NULL); + break; case TARGET_UMTX_OP_WAKE: - operation = UMTX_OP_WAKE; - object = g2h(obj); - ret = get_errno(_umtx_op(g2h(obj), UMTX_OP_WAKE, - val, 0, 0)); + /* args: obj *, nr_wakeup */ + ret = do_umtx_op_wake(obj, val); break; - case TARGET_UMTX_OP_MUTEX_TRYLOCK: case TARGET_UMTX_OP_MUTEX_LOCK: + thr_self(&tid); + if (target_ts) { + if (target_to_host_timespec(&ts, target_ts)) + goto efault; + ret = do_lock_umutex(obj, tid, &ts, 0); + } else { + ret = do_lock_umutex(obj, tid, NULL, 0); + } + break; + case TARGET_UMTX_OP_MUTEX_UNLOCK: + thr_self(&tid); + ret = do_unlock_umutex(obj, tid); + break; + + case TARGET_UMTX_OP_MUTEX_TRYLOCK: + thr_self(&tid); + ret = do_lock_umutex(obj, tid, NULL, TARGET_UMUTEX_TRY); + break; + + case TARGET_UMTX_OP_MUTEX_WAIT: + thr_self(&tid); + if (target_ts) { + if (target_to_host_timespec(&ts, target_ts)) + goto efault; + ret = do_lock_umutex(obj, tid, &ts, + TARGET_UMUTEX_WAIT); + } else { + ret = do_lock_umutex(obj, tid, NULL, + TARGET_UMUTEX_WAIT); + } + break; + + case TARGET_UMTX_OP_MUTEX_WAKE: + /* Don't need to do access_ok(). */ + ret = get_errno(_umtx_op(g2h(obj), UMTX_OP_MUTEX_WAKE, + val, NULL, NULL)); + break; + case TARGET_UMTX_OP_SET_CEILING: + ret = 0; /* XXX quietly ignore these things for now */ + break; + case TARGET_UMTX_OP_CV_WAIT: + /* + * Initialization of the struct conv is done by + * bzero'ing everything in userland. + */ + if (target_ts) { + if (target_to_host_timespec(&ts, target_ts)) + goto efault; + ret = do_cv_wait(obj, uaddr, &ts, val); + } else { + ret = do_cv_wait(obj, uaddr, NULL, val); + } + break; + case TARGET_UMTX_OP_CV_SIGNAL: + /* + * XXX + * User code may check if c_has_waiters is zero. Other + * than that it is assume that user code doesn't do + * much with the struct conv fields and is pretty + * much opauque to userland. + */ + ret = do_cv_signal(obj); + break; + case TARGET_UMTX_OP_CV_BROADCAST: + /* + * XXX + * User code may check if c_has_waiters is zero. Other + * than that it is assume that user code doesn't do + * much with the struct conv fields and is pretty + * much opauque to userland. + */ + ret = do_cv_broadcast(obj); + break; + case TARGET_UMTX_OP_WAIT_UINT: + if (! access_ok(VERIFY_READ, obj, sizeof(abi_ulong))) + goto efault; + if (target_ts) { + if (target_to_host_timespec(&ts, target_ts)) + goto efault; + ret = get_errno(_umtx_op(g2h(obj), + UMTX_OP_WAIT_UINT, + tswap32((uint32_t)val), NULL, &ts)); + } else + ret = get_errno(_umtx_op(g2h(obj), + UMTX_OP_WAIT_UINT, + tswap32((uint32_t)val), NULL, NULL)); + + break; + + case TARGET_UMTX_OP_WAIT_UINT_PRIVATE: + if (! access_ok(VERIFY_READ, obj, sizeof(abi_ulong))) + goto efault; + if (target_ts) { + if (target_to_host_timespec(&ts, target_ts)) + goto efault; + ret = get_errno(_umtx_op(g2h(obj), + UMTX_OP_WAIT_UINT_PRIVATE, + tswap32((uint32_t)val), NULL, &ts)); + } else + ret = get_errno(_umtx_op(g2h(obj), + UMTX_OP_WAIT_UINT_PRIVATE, + tswap32((uint32_t)val), NULL, NULL)); + + break; + + case TARGET_UMTX_OP_WAKE_PRIVATE: + /* Don't need to do access_ok(). */ + ret = get_errno(_umtx_op(g2h(obj), UMTX_OP_WAKE_PRIVATE, + val, NULL, NULL)); + break; + + case TARGET_UMTX_OP_NWAKE_PRIVATE: + if (! access_ok(VERIFY_READ, obj, + val * sizeof(uint32_t))) + goto efault; + ret = get_errno(_umtx_op(g2h(obj), UMTX_OP_NWAKE_PRIVATE, + val, NULL, NULL)); + break; + + case TARGET_UMTX_OP_RW_RDLOCK: + if (target_ts) { + if (target_to_host_timespec(&ts, target_ts)) + goto efault; + ret = do_rw_rdlock(obj, val, &ts); + } else + ret = do_rw_rdlock(obj, val, NULL); + break; + case TARGET_UMTX_OP_RW_WRLOCK: + if (target_ts) { + if (target_to_host_timespec(&ts, target_ts)) + goto efault; + ret = do_rw_wrlock(obj, val, &ts); + } else + ret = do_rw_wrlock(obj, val, NULL); + break; + case TARGET_UMTX_OP_RW_UNLOCK: - case TARGET_UMTX_OP_WAIT_UINT_PRIVATE: - case TARGET_UMTX_OP_WAKE_PRIVATE: - case TARGET_UMTX_OP_MUTEX_WAIT: - case TARGET_UMTX_OP_MUTEX_WAKE: + ret = do_rw_unlock(obj); + break; + +#ifdef UMTX_OP_MUTEX_WAKE2 + case TARGET_UMTX_OP_MUTEX_WAKE2: + if (! access_ok(VERIFY_WRITE, obj, + sizeof(struct target_ucond))) { + goto efault; + } + ret = get_errno(_umtx_op(g2h(obj), + UMTX_OP_MUTEX_WAKE2, val, NULL, NULL)); + break; +#endif + case TARGET_UMTX_OP_SEM_WAIT: + /* XXX Assumes struct _usem is opauque to the user */ + if (! access_ok(VERIFY_WRITE, obj, + sizeof(struct target__usem))) { + goto efault; + } + if (target_ts) { + if (target_to_host_timespec(&ts, target_ts)) + goto efault; + ret = get_errno(_umtx_op(g2h(obj), + UMTX_OP_SEM_WAIT, 0, NULL, &ts)); + } else { + ret = get_errno(_umtx_op(g2h(obj), + UMTX_OP_SEM_WAIT, 0, NULL, NULL)); + } + break; + case TARGET_UMTX_OP_SEM_WAKE: - case TARGET_UMTX_OP_NWAKE_PRIVATE: + /* Don't need to do access_ok(). */ + ret = get_errno(_umtx_op(g2h(obj), UMTX_OP_SEM_WAKE, + val, NULL, NULL)); + break; + default: ret = -TARGET_EINVAL; break; @@ -4761,21 +6414,150 @@ do_stat: } break; + case TARGET_FREEBSD_NR_getfh: + if (!(p = lock_user_string(arg1))) + goto efault; + ret = do_getfh(path(p), arg2); + unlock_user(p, arg1, 0); + break; + + case TARGET_FREEBSD_NR_lgetfh: + if (!(p = lock_user_string(arg1))) + goto efault; + ret = do_lgetfh(path(p), arg2); + unlock_user(p, arg1, 0); + break; + + case TARGET_FREEBSD_NR_fhopen: + ret = do_fhopen(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_fhstat: + ret = do_fhstat(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_fhstatfs: + ret = do_fhstatfs(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_getfsstat: + ret = do_getfsstat(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_statfs: + if (!(p = lock_user_string(arg1))) + goto efault; + ret = do_statfs(path(p), arg2); + unlock_user(p, arg1, 0); + break; + + case TARGET_FREEBSD_NR_fstatfs: + ret = do_fstatfs(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_ioctl: + ret = do_ioctl(arg1, arg2, arg3); + break; + + case TARGET_FREEBSD_NR_kenv: + { + char *n, *v; + + if (!(n = lock_user_string(arg2))) + goto efault; + if (!(v = lock_user_string(arg3))) + goto efault; + ret = get_errno(kenv(arg1, n, v, arg4)); + unlock_user(v, arg3, 0); + unlock_user(n, arg2, 0); + } + break; + + case TARGET_FREEBSD_NR_swapon: + if (!(p = lock_user_string(arg1))) + goto efault; + ret = get_errno(swapon(path(p))); + unlock_user(p, arg1, 0); + break; + + case TARGET_FREEBSD_NR_swapoff: + if (!(p = lock_user_string(arg1))) + goto efault; + ret = get_errno(swapoff(path(p))); + unlock_user(p, arg1, 0); + break; + + case TARGET_FREEBSD_NR_reboot: + ret = get_errno(reboot(arg1)); + break; + + case TARGET_FREEBSD_NR_uuidgen: + ret = do_uuidgen(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_mincore: + if (!(p = lock_user(VERIFY_WRITE, arg3, arg2, 0))) + goto efault; + ret = get_errno(mincore(g2h(arg1), arg2, p)); + unlock_user(p, arg3, ret); + break; + + case TARGET_FREEBSD_NR_adjtime: + ret = do_adjtime(arg1, arg2); + break; + + case TARGET_FREEBSD_NR_ntp_adjtime: + ret = do_ntp_adjtime(arg1); + break; + + case TARGET_FREEBSD_NR_ntp_gettime: + ret = do_ntp_gettime(arg1); + break; + + case TARGET_FREEBSD_NR_vadvise: + ret = -TARGET_EINVAL; /* See sys_ovadvise() in vm_unix.c */ + break; + + case TARGET_FREEBSD_NR_sbrk: + ret = -TARGET_EOPNOTSUPP; /* see sys_sbrk() in vm_mmap.c */ + break; + + case TARGET_FREEBSD_NR_sstk: + ret = -TARGET_EOPNOTSUPP; /* see sys_sstk() in vm_mmap.c */ + break; + case TARGET_FREEBSD_NR_yield: + case TARGET_FREEBSD_NR_sched_yield: + ret = get_errno(sched_yield()); + break; + case TARGET_FREEBSD_NR_sched_setparam: + ret = do_sched_setparam(arg1, arg2); + break; + case TARGET_FREEBSD_NR_sched_getparam: + ret = do_sched_getparam(arg1, arg2); + break; + case TARGET_FREEBSD_NR_sched_setscheduler: + ret = do_sched_setscheduler(arg1, arg2, arg3); + break; + case TARGET_FREEBSD_NR_sched_getscheduler: - case TARGET_FREEBSD_NR_sched_yield: + ret = get_errno(sched_getscheduler(arg1)); + break; + case TARGET_FREEBSD_NR_sched_get_priority_max: - case TARGET_FREEBSD_NR_sched_get_priority_min: - case TARGET_FREEBSD_NR_sched_rr_get_interval: + ret = get_errno(sched_get_priority_max(arg1)); + break; - case TARGET_FREEBSD_NR_reboot: - case TARGET_FREEBSD_NR_shutdown: + case TARGET_FREEBSD_NR_sched_get_priority_min: + ret = get_errno(sched_get_priority_min(arg1)); + break; - case TARGET_FREEBSD_NR_swapon: - case TARGET_FREEBSD_NR_swapoff: + case TARGET_FREEBSD_NR_sched_rr_get_interval: + ret = do_sched_rr_get_interval(arg1, arg2); + break; case TARGET_FREEBSD_NR_cpuset: case TARGET_FREEBSD_NR_cpuset_getid: @@ -4789,22 +6571,10 @@ do_stat: case TARGET_FREEBSD_NR_rctl_remove_rule: case TARGET_FREEBSD_NR_rctl_get_limits: - case TARGET_FREEBSD_NR_ntp_adjtime: - case TARGET_FREEBSD_NR_ntp_gettime: - case TARGET_FREEBSD_NR_sctp_peeloff: case TARGET_FREEBSD_NR_sctp_generic_sendmsg: case TARGET_FREEBSD_NR_sctp_generic_recvmsg: - case TARGET_FREEBSD_NR_getfh: - case TARGET_FREEBSD_NR_lgetfh: - case TARGET_FREEBSD_NR_fhstatfs: - case TARGET_FREEBSD_NR_fhopen: - case TARGET_FREEBSD_NR_fhstat: - - case TARGET_FREEBSD_NR_getfsstat: - case TARGET_FREEBSD_NR_fstatfs: - case TARGET_FREEBSD_NR_modfnext: case TARGET_FREEBSD_NR_modfind: case TARGET_FREEBSD_NR_kldload: @@ -4821,8 +6591,6 @@ do_stat: case TARGET_FREEBSD_NR_quota: #endif - case TARGET_FREEBSD_NR_adjtime: - #ifdef TARGET_FREEBSD_NR_gethostid case TARGET_FREEBSD_NR_gethostid: #endif @@ -4833,13 +6601,6 @@ do_stat: case TARGET_FREEBSD_NR_sethostname: #endif - case TARGET_FREEBSD_NR_mincore: - - case TARGET_FREEBSD_NR_vadvise: - - case TARGET_FREEBSD_NR_sbrk: - case TARGET_FREEBSD_NR_sstk: - #ifdef TARGET_FREEBSD_NR_getkerninfo case TARGET_FREEBSD_NR_getkerninfo: #endif @@ -4847,8 +6608,6 @@ do_stat: case TARGET_FREEBSD_NR_getpagesize: #endif - case TARGET_FREEBSD_NR_revoke: - case TARGET_FREEBSD_NR_profil: case TARGET_FREEBSD_NR_ktrace: @@ -4857,12 +6616,13 @@ do_stat: case TARGET_FREEBSD_NR_jail_get: case TARGET_FREEBSD_NR_jail_set: case TARGET_FREEBSD_NR_jail_remove: + ret = unimplemented(num); + break; case TARGET_FREEBSD_NR_cap_enter: case TARGET_FREEBSD_NR_cap_getmode: - - case TARGET_FREEBSD_NR_kenv: - case TARGET_FREEBSD_NR_uuidgen: + ret = unimplemented(num); + break; case TARGET_FREEBSD_NR___mac_get_proc: case TARGET_FREEBSD_NR___mac_set_proc: @@ -4873,6 +6633,8 @@ do_stat: case TARGET_FREEBSD_NR___mac_get_link: case TARGET_FREEBSD_NR___mac_set_link: case TARGET_FREEBSD_NR_mac_syscall: + ret = unimplemented(num); + break; case TARGET_FREEBSD_NR_audit: case TARGET_FREEBSD_NR_auditon: @@ -4881,6 +6643,8 @@ do_stat: case TARGET_FREEBSD_NR_getaudit_addr: case TARGET_FREEBSD_NR_setaudit_addr: case TARGET_FREEBSD_NR_auditctl: + ret = unimplemented(num); + break; #ifdef TARGET_FREEBSD_NR_obreak @@ -4894,7 +6658,6 @@ do_stat: case TARGET_FREEBSD_NR_sendfile: case TARGET_FREEBSD_NR_ptrace: case TARGET_FREEBSD_NR_utrace: - case TARGET_FREEBSD_NR_ioctl: ret = unimplemented(num); break; @@ -5061,4 +6824,43 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1, void syscall_init(void) { + IOCTLEntry *ie; + const argtype *arg_type; + int size; + +#define STRUCT(name, ...) thunk_register_struct(STRUCT_ ## name, #name, struct_ ## name ## _def); +#define STRUCT_SPECIAL(name) thunk_register_struct_direct(STRUCT_ ## name, #name, &struct_ ## name ## _def); +#ifdef __FreeBSD__ +#include "freebsd/syscall_types.h" +#else +#warning No syscall_types.h +#endif +#undef STRUCT +#undef STRUCT_SPECIAL + + /* + * Patch the ioctl size if necessary using the fact that no + * ioctl has all the bits at '1' in the size field + * (IOCPARM_MAX - 1). + */ + ie = ioctl_entries; + while (ie->target_cmd != 0) { + if (((ie->target_cmd >> TARGET_IOCPARM_SHIFT) & + TARGET_IOCPARM_MASK) == TARGET_IOCPARM_MASK) { + arg_type = ie->arg_type; + if (arg_type[0] != TYPE_PTR) { + fprintf(stderr, + "cannot patch size for ioctl 0x%x\n", + ie->target_cmd); + exit(1); + } + arg_type++; + size = thunk_type_size(arg_type, 0); + ie->target_cmd = (ie->target_cmd & ~(TARGET_IOCPARM_MASK + << TARGET_IOCPARM_SHIFT)) | + (size << TARGET_IOCPARM_SHIFT); + } + ie++; + } + } diff --git a/bsd-user/syscall_defs.h b/bsd-user/syscall_defs.h index 2879d83..3eb760b 100644 --- a/bsd-user/syscall_defs.h +++ b/bsd-user/syscall_defs.h @@ -555,7 +555,7 @@ struct target_rtprio { */ struct target_umtx { - uint32_t u_owner; /* Owner of the mutex. */ + abi_ulong u_owner; /* Owner of the mutex. */ }; struct target_umutex { @@ -573,7 +573,7 @@ struct target_ucond { }; struct target_urwlock { - int32_t rw_state; + uint32_t rw_state; uint32_t rw_flags; uint32_t rw_blocked_readers; uint32_t rw_blocked_writers; @@ -613,7 +613,146 @@ struct target__usem { #define TARGET_UMTX_OP_SEM_WAIT 19 #define TARGET_UMTX_OP_SEM_WAKE 20 #define TARGET_UMTX_OP_NWAKE_PRIVATE 21 -#define TARGET_UMTX_OP_MAX 22 +#define TARGET_UMTX_OP_MUTEX_WAKE2 22 +#define TARGET_UMTX_OP_MAX 23 /* flags for UMTX_OP_CV_WAIT */ -#define TARGET_CHECK_UNPARKING 0x01 +#define TARGET_CVWAIT_CHECK_UNPARKING 0x01 +#define TARGET_CVWAIT_ABSTIME 0x02 +#define TARGET_CVWAIT_CLOCKID 0x04 + +#define TARGET_UMTX_UNOWNED 0x0 +#define TARGET_UMUTEX_UNOWNED 0x0 +#define TARGET_UMTX_CONTESTED (abi_long)(0x8000000000000000) +#define TARGET_UMUTEX_CONTESTED 0x80000000U + +/* flags for umutex */ +#define TARGET_UMUTEX_ERROR_CHECK 0x0002 /* Error-checking mutex */ +#define TARGET_UMUTEX_PRIO_INHERIT 0x0004 /* Priority inherited mutex */ +#define TARGET_UMUTEX_PRIO_PROTECT 0x0008 /* Priority protect mutex */ + +#define TARGET_UMUTEX_TRY 1 +#define TARGET_UMUTEX_WAIT 2 + +/* urwlock flags */ +#define TARGET_URWLOCK_PREFER_READER 0x0002 +#define TARGET_URWLOCK_WRITE_OWNER 0x80000000U +#define TARGET_URWLOCK_WRITE_WAITERS 0x40000000U +#define TARGET_URWLOCK_READ_WAITERS 0x20000000U +#define TARGET_URWLOCK_MAX_READERS 0x1fffffffU +#define TARGET_URWLOCK_READER_COUNT(c) ((c) & TARGET_URWLOCK_MAX_READERS) + +/* mount.h statfs */ +/* + * filesystem id type + */ +typedef struct target_fsid { int32_t val[2]; } target_fsid_t; + +/* + * filesystem statistics + */ +#define TARGET_MFSNAMELEN 16 /* length of type name include null */ +#define TARGET_MNAMELEN 88 /* size of on/from name bufs */ +#define TARGET_STATFS_VERSION 0x20030518 /* current version number */ +struct target_statfs { + uint32_t f_version; /* structure version number */ + uint32_t f_type; /* type of filesystem */ + uint64_t f_flags; /* copy of mount exported flags */ + uint64_t f_bsize; /* filesystem fragment size */ + uint64_t f_iosize; /* optimal transfer block size */ + uint64_t f_blocks; /* total data blocks in filesystem */ + uint64_t f_bfree; /* free blocks in filesystem */ + int64_t f_bavail; /* free blocks avail to non-superuser */ + uint64_t f_files; /* total file nodes in filesystem */ + int64_t f_ffree; /* free nodes avail to non-superuser */ + uint64_t f_syncwrites; /* count of sync writes since mount */ + uint64_t f_asyncwrites; /* count of async writes since mount */ + uint64_t f_syncreads; /* count of sync reads since mount */ + uint64_t f_asyncreads; /* count of async reads since mount */ + uint64_t f_spare[10]; /* unused spare */ + uint32_t f_namemax; /* maximum filename length */ + uid_t f_owner; /* user that mounted the filesystem */ + target_fsid_t f_fsid; /* filesystem id */ + char f_charspare[80]; /* spare string space */ + char f_fstypename[TARGET_MFSNAMELEN]; /* filesys type name */ + char f_mntfromname[TARGET_MNAMELEN]; /* mount filesystem */ + char f_mntonname[TARGET_MNAMELEN]; /* dir on which mounted*/ +}; + +/* + * File identifier. + * These are unique per filesystem on a single machine. + */ +#define TARGET_MAXFIDSZ 16 + +struct target_fid { + u_short fid_len; /* len of data in bytes */ + u_short fid_data0; /* force longword align */ + char fid_data[TARGET_MAXFIDSZ]; /* data (variable len) */ +}; + +/* + * Generic file handle + */ +struct target_fhandle { + target_fsid_t fh_fsid; /* Filesystem id of mount point */ + struct target_fid fh_fid; /* Filesys specific id */ +}; +typedef struct target_fhandle target_fhandle_t; + + +/* + * uuidgen. From sys/uuid.h. + */ + +#define TARGET_UUID_NODE_LEN 6 + +struct target_uuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi_and_reserved; + uint8_t clock_seq_low; + uint8_t node[TARGET_UUID_NODE_LEN]; +}; + +/* + * ntp. From sys/timex.h. + */ + +struct target_ntptimeval { + struct target_freebsd_timespec time; + abi_long maxerror; + abi_long esterror; + abi_long tai; + int32_t time_state; +}; + +struct target_timex { + uint32_t modes; + abi_long offset; + abi_long freq; + abi_long maxerror; + abi_long esterror; + int32_t status; + abi_long constant; + abi_long precision; + abi_long tolerance; + + abi_long ppsfreq; + abi_long jitter; + int32_t shift; + abi_long stabil; + abi_long jitcnt; + abi_long calcnt; + abi_long errcnt; + abi_long stbcnt; +}; + +/* + * sched.h From sched.h + */ + +struct target_sched_param { + int32_t sched_priority; +};