/** * \brief Linux emulation code * \author Josef Soentgen * \date 2014-08-04 */ /* * Copyright (C) 2014 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ /* Genode includes */ #include /* local includes */ #include #include # include # include # include # include #include #include static void run_socketcall(void *); /* XXX move Wifi::Socket definition to better location */ struct Wifi::Socket { void *socket = nullptr; bool non_block = false; Socket() { } explicit Socket(void *s) : socket(s) { } }; struct Call { enum Opcode { NONE, SOCKET, CLOSE, BIND, GETSOCKNAME, RECVMSG, SENDMSG, SENDTO, SETSOCKOPT, GET_MAC_ADDRESS, POLL_ALL, NON_BLOCK, }; Opcode opcode = NONE; Wifi::Socket *handle = nullptr; union { struct { int domain; int type; int protocol; void *result; } socket; struct { /* no args */ } close; struct { sockaddr const *addr; int addrlen; } bind; struct { sockaddr *addr; int *addrlen; } getsockname; struct { msghdr msg; int flags; iovec iov[Wifi::Msghdr::MAX_IOV_LEN]; } recvmsg; struct { msghdr msg; int flags; iovec iov[Wifi::Msghdr::MAX_IOV_LEN]; } sendmsg; struct { int level; int optname; void const *optval; uint32_t optlen; } setsockopt; struct { unsigned char *addr; } get_mac_address; struct { Wifi::Poll_socket_fd *sockets; unsigned num; int timeout; } poll_all; struct { bool value; } non_block; }; int err = 0; }; static Call _call; static Genode::Semaphore _block; namespace Lx { class Socket; } /** * Context for socket calls */ class Lx::Socket { private: Genode::Signal_transmitter _sender; Genode::Signal_rpc_member _dispatcher; Lx::Task _task; struct socket *_call_socket() { struct socket *sock = static_cast(_call.handle->socket); if (!sock) PERR("BUG: sock is zero"); return sock; } void _do_socket() { struct socket *s; int res = sock_create_kern(_call.socket.domain, _call.socket.type, _call.socket.protocol, &s); if (!res) { _call.socket.result = s; _call.err = 0; return; } PERR("sock_create_kern failed, res: %d", res); _call.socket.result = nullptr; _call.err = res; } void _do_close() { struct socket *sock = _call_socket(); _call.err = sock->ops->release(sock); } void _do_bind() { struct socket *sock = _call_socket(); _call.err = sock->ops->bind(sock, const_cast(_call.bind.addr), _call.bind.addrlen); } void _do_getsockname() { struct socket *sock = _call_socket(); int addrlen = *_call.getsockname.addrlen; _call.err = sock->ops->getname(sock, _call.getsockname.addr, &addrlen, 0); *_call.getsockname.addrlen = addrlen; } void _do_recvmsg() { struct socket *sock = _call_socket(); struct msghdr *msg = &_call.recvmsg.msg; /* needed by AF_NETLINK */ struct sock_iocb siocb; Genode::memset(&siocb, 0, sizeof(struct sock_iocb)); struct kiocb kiocb; Genode::memset(&kiocb, 0, sizeof(struct kiocb)); kiocb.private_ = &siocb; if (_call.handle->non_block) msg->msg_flags |= MSG_DONTWAIT; size_t iovlen = 0; for (size_t i = 0; i < msg->msg_iovlen; i++) iovlen += msg->msg_iov[i].iov_len; _call.err = sock->ops->recvmsg(&kiocb, sock, msg, iovlen, _call.recvmsg.flags); } void _do_sendmsg() { struct socket *sock = _call_socket(); struct msghdr *msg = const_cast(&_call.sendmsg.msg); /* needed by AF_NETLINK */ struct sock_iocb siocb; Genode::memset(&siocb, 0, sizeof(struct sock_iocb)); struct kiocb kiocb; Genode::memset(&kiocb, 0, sizeof(struct kiocb)); kiocb.private_ = &siocb; if (_call.handle->non_block) msg->msg_flags |= MSG_DONTWAIT; size_t iovlen = 0; for (size_t i = 0; i < msg->msg_iovlen; i++) iovlen += msg->msg_iov[i].iov_len; _call.err = sock->ops->sendmsg(&kiocb, sock, msg, iovlen); } void _do_setsockopt() { struct socket *sock = _call_socket(); /* taken from kernel_setsockopt() in net/socket.c */ if (_call.setsockopt.level == SOL_SOCKET) _call.err = sock_setsockopt(sock, _call.setsockopt.level, _call.setsockopt.optname, (char *)_call.setsockopt.optval, _call.setsockopt.optlen); else _call.err = sock->ops->setsockopt(sock, _call.setsockopt.level, _call.setsockopt.optname, (char *)_call.setsockopt.optval, _call.setsockopt.optlen); } void _do_get_mac_address() { Lx::get_mac_address(_call.get_mac_address.addr); } void _do_poll_all() { Wifi::Poll_socket_fd *sockets = _call.poll_all.sockets; unsigned num = _call.poll_all.num; int timeout = _call.poll_all.timeout; enum { POLLIN_SET = (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR), POLLOUT_SET = (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR), POLLEX_SET = (POLLPRI) }; int nready = 0; bool timeout_triggered = false; bool woken_up = false; do { /** * Timeout was triggered, exit early. */ if (timeout_triggered) break; /** * Poll each socket and check if there is something of interest. */ for (unsigned i = 0; i < num; i++) { struct socket *sock = static_cast(sockets[i].s->socket); int mask = sock->ops->poll(0, sock, 0); sockets[i].revents = 0; if (mask & POLLIN_SET) sockets[i].revents |= sockets[i].events & Wifi::WIFI_POLLIN ? Wifi::WIFI_POLLIN : 0; if (mask & POLLOUT_SET) sockets[i].revents |= sockets[i].events & Wifi::WIFI_POLLOUT ? Wifi::WIFI_POLLOUT : 0; if (mask & POLLEX_SET) sockets[i].revents |= sockets[i].events & Wifi::WIFI_POLLEX ? Wifi::WIFI_POLLEX : 0; if (sockets[i].revents) nready++; } /** * We were woken up but there is still nothing of interest. */ if (woken_up) break; /** * Exit the loop if either a socket is ready or there is * no timeout given. */ if (nready || !timeout) break; /** * In case of a timeout add all sockets to an artificial wait list * so at least one is woken up an sk_data_ready() call. */ Lx::Task *task = Lx::scheduler().current(); struct socket_wq wq[num]; Lx::Task::List wait_list; task->wait_enqueue(&wait_list); for (unsigned i = 0; i < num; i++) { struct socket *sock = static_cast(sockets[i].s->socket); wq[i].wait.list = &wait_list; sock->sk->sk_wq = &wq[i]; } long t = jiffies + msecs_to_jiffies(timeout); timeout_triggered = !schedule_timeout(t); task->wait_dequeue(&wait_list); for (unsigned i = 0; i < num; i++) { struct socket *sock = static_cast(sockets[i].s->socket); sock->sk->sk_wq = 0; } woken_up = true; } while (1); _call.err = nready; } void _do_non_block() { _call.handle->non_block = _call.non_block.value; } void _handle(unsigned) { _task.unblock(); Lx::scheduler().schedule(); } public: Socket(Server::Entrypoint &ep) : _dispatcher(ep, *this, &Lx::Socket::_handle), _task(run_socketcall, nullptr, "socketcall", Lx::Task::PRIORITY_0, Lx::scheduler()) { _sender.context(_dispatcher); } void exec_call() { switch (_call.opcode) { case Call::BIND: _do_bind(); break; case Call::CLOSE: _do_close(); break; case Call::GETSOCKNAME: _do_getsockname(); break; case Call::POLL_ALL: _do_poll_all(); break; case Call::RECVMSG: _do_recvmsg(); break; case Call::SENDMSG: _do_sendmsg(); break; case Call::SETSOCKOPT: _do_setsockopt(); break; case Call::SOCKET: _do_socket(); break; case Call::GET_MAC_ADDRESS: _do_get_mac_address(); break; case Call::NON_BLOCK: _do_non_block(); break; default: _call.err = -EINVAL; PWRN("unknown opcode: %u", _call.opcode); break; } _call.opcode = Call::NONE; _block.up(); } void submit_and_block() { _sender.submit(); _block.down(); } }; static Lx::Socket *_socket; void Lx::socket_init(Server::Entrypoint &ep) { static Lx::Socket socket_ctx(ep); _socket = &socket_ctx; } static void run_socketcall(void *) { while (1) { Lx::scheduler().current()->block_and_schedule(); _socket->exec_call(); } } /************************** ** Socket_call instance ** **************************/ Wifi::Socket_call socket_call; /*************************** ** Socket_call interface ** ***************************/ using namespace Wifi; Wifi::Socket *Socket_call::socket(int domain, int type, int protocol) { /* FIXME domain, type, protocol values */ _call.opcode = Call::SOCKET; _call.socket.domain = domain; _call.socket.type = type; _call.socket.protocol = protocol; _socket->submit_and_block(); if (_call.socket.result == 0) return 0; Wifi::Socket *s = new (Genode::env()->heap()) Wifi::Socket(_call.socket.result); return s; } int Socket_call::close(Socket *s) { _call.opcode = Call::CLOSE; _call.handle = s; _socket->submit_and_block(); if (_call.err) PWRN("error %d on close()", _call.err); destroy(Genode::env()->heap(), s); return 0; } int Socket_call::bind(Socket *s, Wifi::Sockaddr const *addr, unsigned addrlen) { /* FIXME convert to/from Sockaddr */ _call.opcode = Call::BIND; _call.handle = s; _call.bind.addr = (sockaddr const *)addr; _call.bind.addrlen = addrlen; _socket->submit_and_block(); return _call.err; } int Socket_call::getsockname(Socket *s, Wifi::Sockaddr *addr, unsigned *addrlen) { /* FIXME convert to/from Sockaddr */ /* FIXME unsigned * -> int * */ _call.opcode = Call::GETSOCKNAME; _call.handle = s; _call.getsockname.addr = (sockaddr *)addr; _call.getsockname.addrlen = (int *)addrlen; _socket->submit_and_block(); return _call.err; } int Socket_call::poll_all(Poll_socket_fd *s, unsigned num, int timeout) { _call.opcode = Call::POLL_ALL; _call.handle = 0; _call.poll_all.sockets = s; _call.poll_all.num = num; _call.poll_all.timeout = timeout; _socket->submit_and_block(); return _call.err; } static int msg_flags(Wifi::Flags in) { int out = Wifi::WIFI_F_NONE; if (in & Wifi::WIFI_F_MSG_ERRQUEUE) out |= MSG_ERRQUEUE; return out; }; Wifi::ssize_t Socket_call::recvmsg(Socket *s, Wifi::Msghdr *msg, Wifi::Flags flags) { _call.opcode = Call::RECVMSG; _call.handle = s; _call.recvmsg.msg.msg_name = msg->msg_name; _call.recvmsg.msg.msg_namelen = msg->msg_namelen; _call.recvmsg.msg.msg_iov = _call.recvmsg.iov; _call.recvmsg.msg.msg_iovlen = msg->msg_iovlen; _call.recvmsg.msg.msg_control = msg->msg_control; _call.recvmsg.msg.msg_controllen = msg->msg_controllen; _call.recvmsg.flags = msg_flags(flags); for (unsigned i = 0; i < msg->msg_iovlen; ++i) { _call.recvmsg.iov[i].iov_base = msg->msg_iov[i].iov_base; _call.recvmsg.iov[i].iov_len = msg->msg_iov[i].iov_len; } _socket->submit_and_block(); msg->msg_namelen = _call.recvmsg.msg.msg_namelen; return _call.err; } Wifi::ssize_t Socket_call::sendmsg(Socket *s, Wifi::Msghdr const *msg, Wifi::Flags flags) { _call.opcode = Call::SENDMSG; _call.handle = s; _call.sendmsg.msg.msg_name = msg->msg_name; _call.sendmsg.msg.msg_namelen = msg->msg_namelen; _call.sendmsg.msg.msg_iov = _call.sendmsg.iov; _call.sendmsg.msg.msg_iovlen = msg->msg_iovlen; _call.sendmsg.msg.msg_control = 0; _call.sendmsg.msg.msg_controllen = 0; _call.sendmsg.flags = msg_flags(flags); for (unsigned i = 0; i < msg->msg_iovlen; ++i) { _call.sendmsg.iov[i].iov_base = msg->msg_iov[i].iov_base; _call.sendmsg.iov[i].iov_len = msg->msg_iov[i].iov_len; } _socket->submit_and_block(); return _call.err; } static int sockopt_level(Sockopt_level const in) { switch (in) { case Wifi::WIFI_SOL_SOCKET: return SOL_SOCKET; case Wifi::WIFI_SOL_NETLINK: return SOL_NETLINK; } return -1; } static int sockopt_name(Sockopt_level const level, Sockopt_name const in) { switch (level) { case Wifi::WIFI_SOL_SOCKET: switch (in) { case Wifi::WIFI_SO_SNDBUF: return SO_SNDBUF; case Wifi::WIFI_SO_RCVBUF: return SO_RCVBUF; case Wifi::WIFI_SO_PASSCRED: return SO_PASSCRED; case Wifi::WIFI_SO_WIFI_STATUS: return SO_WIFI_STATUS; default: return -1; } case Wifi::WIFI_SOL_NETLINK: switch (in) { case Wifi::WIFI_NETLINK_ADD_MEMBERSHIP: return NETLINK_ADD_MEMBERSHIP; case Wifi::WIFI_NETLINK_DROP_MEMBERSHIP: return NETLINK_DROP_MEMBERSHIP; case Wifi::WIFI_NETLINK_PKTINFO: return NETLINK_PKTINFO; default: return -1; } } return -1; } int Socket_call::setsockopt(Socket *s, Wifi::Sockopt_level level, Wifi::Sockopt_name optname, const void *optval, uint32_t optlen) { /* FIXME optval values */ _call.opcode = Call::SETSOCKOPT; _call.handle = s; _call.setsockopt.level = sockopt_level(level); _call.setsockopt.optname = sockopt_name(level, optname); _call.setsockopt.optval = optval; _call.setsockopt.optlen = optlen; _socket->submit_and_block(); return _call.err; } void Socket_call::non_block(Socket *s, bool value) { _call.opcode = Call::NON_BLOCK; _call.handle = s; _call.non_block.value = value; _socket->submit_and_block(); } void Socket_call::get_mac_address(unsigned char *addr) { _call.opcode = Call::GET_MAC_ADDRESS; _call.handle = 0; _call.get_mac_address.addr = addr; _socket->submit_and_block(); }