diff --git a/Makefile.am b/Makefile.am index 110ad175ab4a188fa98de53593323cb18ac078f1..1a38d4716082442a12ef85c96c2ffe636fb45d41 100644 --- a/Makefile.am +++ b/Makefile.am @@ -17,6 +17,11 @@ mboxd_SOURCES = \ mboxd_LINK = $(LINK) +if ENABLE_UNIX_SOCKET_TRANSPORT +mboxd_SOURCES += \ + control_unix.c +endif + if HAVE_MBOX_TRANSPORT mboxd_SOURCES += \ transport_mbox.c @@ -32,6 +37,8 @@ endif mboxd_LDFLAGS = $(LIBSYSTEMD_LIBS) mboxd_CFLAGS = $(LIBSYSTEMD_CFLAGS) +include_HEADERS = $(srcdir)/include/hiomapd-socket.h + # MTD Backing storage include mtd/Makefile.am.include @@ -59,7 +66,9 @@ check_PROGRAMS = XFAIL_TESTS = AM_LIBS = $(CODE_COVERAGE_LIBS) -AM_CPPFLAGS = $(CODE_COVERAGE_CPPFLAGS) -UNDEBUG +AM_CPPFLAGS = $(CODE_COVERAGE_CPPFLAGS) -UNDEBUG -I$(srcdir)/include + + AM_CFLAGS = $(CODE_COVERAGE_CFLAGS) AM_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS) diff --git a/configure.ac b/configure.ac index 6d487f781d2df9f5d3b505c7610ca8efe5876907..830c460d4f7069df9c151ad1c850c7af872c5643 100644 --- a/configure.ac +++ b/configure.ac @@ -45,6 +45,13 @@ AS_IF([test "x$enable_oe_sdk" == "xyes"], AC_SUBST([OESDK_TESTCASE_FLAGS], [$testcase_flags]) ) +AC_ARG_ENABLE([unix-socket-transport], + AS_HELP_STRING([--enable-unix-socket-transport], [Turn on unix socket control interface])) + +AS_IF([test "x$enable_unix_socket_transport" == "xyes"], + AM_CONDITIONAL(ENABLE_UNIX_SOCKET_TRANSPORT, true), + AM_CONDITIONAL(ENABLE_UNIX_SOCKET_TRANSPORT, false)) + AC_ARG_ENABLE([virtual-pnor], AS_HELP_STRING([--enable-virtual-pnor], [Turn on virtual pnor]) [ diff --git a/control_unix.c b/control_unix.c new file mode 100644 index 0000000000000000000000000000000000000000..fc5cb133c48f2d265553316f3d69cea35d345683 --- /dev/null +++ b/control_unix.c @@ -0,0 +1,549 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include "mboxd.h" +#include "common.h" +#include "protocol.h" + +#include "hiomapd-socket.h" +struct ep_ctx; +struct hust_context; + +struct ep_ops { + int (*work)(struct hust_context *, struct ep_ctx*, struct epoll_event*); +}; + +struct ep_ctx { + int fd; + struct ep_ops *ops; +}; + +static const struct transport_ops hust_ops; + + +struct hust_context { + struct mbox_context *context; + struct ops_container w; + int hustfd; +}; + +static inline struct hust_context* +container_to_uctx(struct ops_container *container) +{ + return container_of(container, struct hust_context, w); +} + + +int husc_ping_hdlr(struct hust_context *, struct ep_ctx *, struct unix_hiomapd_request *); + +/* transport handlers */ +/* autogen'd with: + * + for type in reset getinfo getflashinfo closewindow markdirty flush asc erase flashcontrollost daemonready protocolreset windowreset; do +echo "static int hust_${type}_hdlr(struct hust_context *, struct ep_ctx *, + struct unix_hiomapd_request *);" +done + + * + */ +static int hust_reset_hdlr(struct hust_context *, struct ep_ctx *, + struct unix_hiomapd_request *); +static int hust_getinfo_hdlr(struct hust_context *, struct ep_ctx *, + struct unix_hiomapd_request *); +static int hust_getflashinfo_hdlr(struct hust_context *, struct ep_ctx *, + struct unix_hiomapd_request *); +static int hust_closewindow_hdlr(struct hust_context *, struct ep_ctx *, + struct unix_hiomapd_request *); +static int hust_markdirty_hdlr(struct hust_context *, struct ep_ctx *, + struct unix_hiomapd_request *); +static int hust_flush_hdlr(struct hust_context *, struct ep_ctx *, + struct unix_hiomapd_request *); +static int hust_ack_hdlr(struct hust_context *, struct ep_ctx *, + struct unix_hiomapd_request *); +static int hust_erase_hdlr(struct hust_context *, struct ep_ctx *, + struct unix_hiomapd_request *); +static int hust_flashcontrollost_hdlr(struct hust_context *, struct ep_ctx *, + struct unix_hiomapd_request *); +static int hust_daemonready_hdlr(struct hust_context *, struct ep_ctx *, + struct unix_hiomapd_request *); +static int hust_protocolreset_hdlr(struct hust_context *, struct ep_ctx *, + struct unix_hiomapd_request *); +static int hust_windowreset_hdlr(struct hust_context *, struct ep_ctx *, + struct unix_hiomapd_request *); + +static int hust_createreadwindow_hdlr(struct hust_context *, struct ep_ctx *, + struct unix_hiomapd_request *); + +static int hust_createwritewindow_hdlr(struct hust_context *, struct ep_ctx *, + struct unix_hiomapd_request *); + +struct { + int (*hdlr)(struct hust_context *, struct ep_ctx *, struct unix_hiomapd_request *); +} husc_dispatch_lut[] = { + + [husc_ping] = { .hdlr = husc_ping_hdlr,}, + + [hust_createreadwindow] = { .hdlr = hust_createreadwindow_hdlr,}, + [hust_createwritewindow] = { .hdlr = hust_createwritewindow_hdlr,}, + + [hust_reset] = { .hdlr = hust_reset_hdlr,}, + [hust_getinfo] = { .hdlr = hust_getinfo_hdlr,}, + [hust_getflashinfo] = { .hdlr = hust_getflashinfo_hdlr,}, + [hust_closewindow] = { .hdlr = hust_closewindow_hdlr,}, + [hust_markdirty] = { .hdlr = hust_markdirty_hdlr,}, + [hust_flush] = { .hdlr = hust_flush_hdlr,}, + [hust_ack] = { .hdlr = hust_ack_hdlr,}, + [hust_erase] = { .hdlr = hust_erase_hdlr,}, + [hust_flashcontrollost] = { .hdlr = hust_flashcontrollost_hdlr,}, + [hust_daemonready] = { .hdlr = hust_daemonready_hdlr,}, + [hust_protocolreset] = { .hdlr = hust_protocolreset_hdlr,}, + [hust_windowreset] = { .hdlr = hust_windowreset_hdlr,}, + +}; + +int epoll_fd_work(struct hust_context *, struct ep_ctx*, struct epoll_event *); +int conn_fd_work(struct hust_context *, struct ep_ctx*, struct epoll_event *); +struct ep_ops listen_socket_ops = { + .work = epoll_fd_work, +}; + +struct ep_ops conn_ops = { + .work = conn_fd_work, +}; + +int transport_unix_dispatch(struct ops_container *container, + struct epoll_event *evt) +{ + struct hust_context *uctx = container_to_uctx(container); + struct mbox_context *context = uctx->context; + const static size_t evp_count = 0x10; + /* Having more than one client at a time is rather unlikely, + * so this is probably overkill*/ + struct epoll_event evps[0x10] = { 0 }; + struct ep_ctx *ectx; + int count; + int i; + + /* First event should be an out event, that means we need to do + * some late setup. */ + if (evt->events & EPOLLOUT) + { + struct epoll_event evx = { + .events = EPOLLIN, + .data = { .ptr = evt->data.ptr, }, + }; + MSG_DBG("Unix socket out event\n"); + epoll_ctl(context->epollfd, EPOLL_CTL_MOD, uctx->hustfd, &evx); + return protocol_events_put(context, container); + } + + if (!(evt->events & POLLIN)) + { + MSG_ERR("Unix socket event, but it's an invalid type\n"); + return -EINVAL; + } + MSG_DBG("Unix socket Event\n"); + + count = epoll_wait(uctx->hustfd, evps, evp_count, 0); + MSG_DBG("epoll_wait(): %d(%m)\n", count); + for (i = 0; i < count; i++) + { + int rc; + ectx = evps[i].data.ptr; + MSG_DBG("fd[%d].epoll_event.event: %08x\n", ectx->fd, evps[i].events); + /* NOTE: ectx might be freed after this call + * set it to null to be explicit about it*/ + /* It's debatable if we should forward this return code back, + * since were in a loop of non-interlocking things. + * however it probably doesn't matter because we are not a + * high performance server (the though of this needing high + * throughput is horrifying)*/ + rc = ectx->ops->work(uctx, ectx, evps + i); + ectx = NULL; + } +} + + +int init_unix(struct mbox_context *context, struct transport_ops *ops) +{ + struct sockaddr_un sa = { + .sun_family = AF_UNIX, + .sun_path = "/var/run/hiomapd" + }; + struct hust_context *uctx = calloc(1, sizeof(*uctx)); + int usfd = socket(AF_UNIX, SOCK_SEQPACKET ,0); + int rc; + int epfd = epoll_create1(EPOLL_CLOEXEC); + struct epoll_event evp = { + .events = EPOLLIN, + }; + struct ep_ctx *ectx; + + if (!uctx) { + MSG_ERR("Failed to allocate unix socket transport context\n"); + close(epfd); + return -ENOMEM; + } + + uctx->context = context; + uctx->w.ops = ops; + + MSG_DBG("%s:%d\n", __func__, __LINE__); + /* destroy an existing socket, otherwise we couldn't bind*/ + unlink(sa.sun_path); + + /* As a reminder: we use the void cast to promise the compiler: + * 'yes this is totally aligned correctly, so forget your knowledge + * of it's aligment requirement' + */ + if ((rc = bind(usfd, (struct sockaddr *)(void*)&sa, sizeof(sa)))) + goto out_bind; + if ((rc = listen(usfd, 8))) + goto out_listen; + + + ectx = calloc(1, sizeof(ectx)); + ectx->fd = usfd; + ectx->ops = &listen_socket_ops; + evp.data.ptr = ectx; + epoll_ctl(epfd, EPOLL_CTL_ADD, usfd, &evp); + + uctx->hustfd = epfd; + /* Set as the default transport, but only + * if no other transports have registered */ + if (!context->transport) + context->transport = &uctx->w; + + register_event_fd(uctx->hustfd, context, &uctx->w); + return 0; +out_listen: +out_bind: + return -1; +} + +int epoll_fd_work(struct hust_context *uctx, struct ep_ctx* ectx, struct epoll_event *ev) +{ + struct epoll_event evp = { + .events = EPOLLIN, + }; + struct ep_ctx *nectx; + int new_fd; + new_fd = accept4(ectx->fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC); + ectx = calloc(1, sizeof(ectx)); + ectx->fd = new_fd; + ectx->ops = &conn_ops; + evp.data.ptr = ectx; + epoll_ctl(uctx->hustfd, EPOLL_CTL_ADD, new_fd, &evp); + + return 0; +} + +int conn_fd_work(struct hust_context *uctx, struct ep_ctx* ectx, struct epoll_event *ev) +{ + MSG_DBG("%s:%d\n", __func__, __LINE__); + struct unix_hiomapd_request req = { 0 }; + ssize_t readlen; + if ((ev->events & EPOLLIN) && + (readlen = read(ectx->fd, &req, sizeof(req)))) + { + MSG_DBG("Got request sized %08x\n", (uint32_t)readlen); + MSG_DBG("Got request: %08x\n", req.request); + if ((readlen >= 4) && (req.request < husx_api_limit) && + (husc_dispatch_lut[req.request].hdlr)) + { + int rc; + rc = husc_dispatch_lut[req.request].hdlr(uctx, ectx, &req); + if (rc) + MSG_DBG("WARNING: cmd %x return %x\n", req.request, rc); + return 0; + } + return -1; + } + if (ev->events & EPOLLHUP) + { + close(ectx->fd); + free(ectx); + return 0; + } + + MSG_DBG("Got unhandled request: %08x\n", req.request); + return -1; + +} + +int husc_ping_hdlr(struct hust_context * uctx, struct ep_ctx * ectx, + struct unix_hiomapd_request *req) +{ +} + +int hust_createreadwindow_hdlr(struct hust_context * uctx, struct ep_ctx *ectx, + struct unix_hiomapd_request *req) +{ + struct mbox_context *context = uctx->context; + struct protocol_create_window io = { 0 }; + int rc; + io.req.offset = req->args[0] & 0xffff; + io.req.size = req->args[1] & 0xffff; + MSG_DBG("%s:%d\n", __func__, __LINE__); + + io.req.ro = true; + if ((rc = context->protocol->create_window(context, &io)) < 0) + return rc; + + memset(req, 0, sizeof(*req)); + req->request = 1; + req->args[0] = context->bmc_events; + req->args[2] = io.resp.lpc_address; + req->args[3] = io.resp.size; + req->args[4] = io.resp.offset; + req->args[1] = 4; + write(ectx->fd, req, sizeof(*req)); + + return rc; +} + + +int hust_createwritewindow_hdlr(struct hust_context * uctx, struct ep_ctx *ectx, + struct unix_hiomapd_request *req) +{ + struct mbox_context *context = uctx->context; + struct protocol_create_window io = { 0 }; + int rc; + io.req.offset = req->args[0] & 0xffff; + io.req.size = req->args[1] & 0xffff; + + io.req.ro = false; + if ((rc = context->protocol->create_window(context, &io)) < 0) + return rc; + + memset(req, 0, sizeof(*req)); + req->request = 1; + req->args[0] = context->bmc_events; + req->args[2] = io.resp.lpc_address; + req->args[3] = io.resp.size; + req->args[4] = io.resp.offset; + req->args[1] = 4; + write(ectx->fd, req, sizeof(*req)); + + return rc; +} + + +static int hust_closewindow_hdlr(struct hust_context * uctx, struct ep_ctx *ectx, + struct unix_hiomapd_request *req) +{ + struct mbox_context *context = uctx->context; + struct protocol_close io = { 0 }; + int rc; + io.req.flags = req->args[0] & 0xff; + + rc = context->protocol->close(context, &io); + + memset(req, 0, sizeof(*req)); + req->request = 1; + req->args[0] = context->bmc_events; + write(ectx->fd, req, sizeof(*req)); + + return rc; +} + +static int hust_reset_hdlr(struct hust_context *uctx, struct ep_ctx *ectx, + struct unix_hiomapd_request *req) +{ + struct mbox_context *context = uctx->context; + int rc; + if ((rc = context->protocol->reset(context)) < 0) + return rc; + + memset(req, 0, sizeof(*req)); + req->request = 1; + req->args[0] = context->bmc_events; + req->args[1] = 0; + write(ectx->fd, req, sizeof(*req)); + + return rc; +} + +static int hust_getinfo_hdlr(struct hust_context *uctx, struct ep_ctx *ectx, + struct unix_hiomapd_request *req) +{ + struct mbox_context *context = uctx->context; + struct protocol_get_info io; + int rc; + io.req.api_version = req->args[0]; + if ((rc = context->protocol->get_info(context, &io)) < 0) + return rc; + + /* We need to do this before sending a reply since otherwise the + * bmc events will be.. poorly defined */ + context->transport = &uctx->w; + protocol_events_set(context, context->bmc_events); + memset(req, 0, sizeof(*req)); + req->request = 1; + req->args[0] = context->bmc_events; + req->args[2] = io.resp.api_version; + req->args[1] = 1; + if (io.resp.api_version >= 2) + { + req->args[3] = io.resp.v2.block_size_shift; + req->args[4] = io.resp.v2.timeout; + req->args[1] += 2; + } + write(ectx->fd, req, sizeof(*req)); + + return rc; +} + +static int hust_getflashinfo_hdlr(struct hust_context *uctx, struct ep_ctx *ectx, + struct unix_hiomapd_request *req) +{ + struct mbox_context *context = uctx->context; + struct protocol_get_flash_info io; + int rc; + if ((rc = context->protocol->get_flash_info(context, &io)) < 0) + return rc; + + memset(req, 0, sizeof(*req)); + req->request = 1; + req->args[0] = context->bmc_events; + req->args[2] = io.resp.v2.flash_size; + req->args[3] = io.resp.v2.erase_size; + req->args[1] = 2; + write(ectx->fd, req, sizeof(*req)); + + return rc; +} + +static int hust_markdirty_hdlr(struct hust_context *uctx, struct ep_ctx *ectx, + struct unix_hiomapd_request *req) +{ + struct mbox_context *context = uctx->context; + struct protocol_mark_dirty io; + int rc; + io.req.v2.offset = req->args[0]; + io.req.v2.size = req->args[1]; + if ((rc = context->protocol->mark_dirty(context, &io)) < 0) + return rc; + memset(req, 0, sizeof(*req)); + req->request = 1; + req->args[0] = context->bmc_events; + write(ectx->fd, req, sizeof(*req)); + + return rc; +} + +static int hust_flush_hdlr(struct hust_context *uctx, struct ep_ctx *ectx, + struct unix_hiomapd_request *req) +{ + struct mbox_context *context = uctx->context; + int rc; + + if ((rc = context->protocol->flush(context, NULL /* No args in v2 */)) < 0) + return rc; + memset(req, 0, sizeof(*req)); + req->request = 1; + req->args[0] = context->bmc_events; + write(ectx->fd, req, sizeof(*req)); + return rc; +} + +static int hust_ack_hdlr(struct hust_context *uctx, struct ep_ctx *ectx, + struct unix_hiomapd_request *req) +{ + struct mbox_context *context = uctx->context; + struct protocol_ack io; + int rc; + + io.req.flags = req->args[0]; + if ((rc = context->protocol->ack(context, &io))) + return rc; + + memset(req, 0, sizeof(*req)); + req->request = 1; + req->args[0] = context->bmc_events; + req->args[1] = 0; + write(ectx->fd, req, sizeof(*req)); + + return rc; +} + +static int hust_erase_hdlr(struct hust_context *uctx, struct ep_ctx *ectx, + struct unix_hiomapd_request *req) +{ + struct mbox_context *context = uctx->context; + struct protocol_erase io; + int rc; + + io.req.offset = req->args[0]; + io.req.size = req->args[1]; + + if ((rc = context->protocol->erase(context, &io))) + return rc; + + memset(req, 0, sizeof(*req)); + req->request = 1; + req->args[0] = context->bmc_events; + write(ectx->fd, req, sizeof(*req)); + + return rc; +} +/* Properties are not, yet, supported */ +static int hust_flashcontrollost_hdlr(struct hust_context *uctx, struct ep_ctx *ectx, + struct unix_hiomapd_request *req) +{ +} + +static int hust_daemonready_hdlr(struct hust_context *uctx, struct ep_ctx *ectx, + struct unix_hiomapd_request *req) +{ +} + +static int hust_protocolreset_hdlr(struct hust_context *uctx, struct ep_ctx *ectx, + struct unix_hiomapd_request *req) +{ +} + +static int hust_windowreset_hdlr(struct hust_context *uctx, struct ep_ctx *ectx, + struct unix_hiomapd_request *req) +{ +} + +static int hust_put_events(struct ops_container *container, uint8_t mask) +{ + /* ATM this a no-op, in the real world, we should probably + * send out a 'response' packet to all the connected sockets... + * however, atm I've no mechanism to do that, and no idea what + * the clients should do even if they recieve such an msg*/ + return 0; +} + +static int hust_set_events(struct ops_container *container, uint8_t events, + uint8_t mask) +{ + /* dbus calls the same func as above for this + * (execpt is sends in (events & mask)) */ + return 0; +} + +static int hust_clear_events(struct ops_container *container, uint8_t events, + uint8_t mask) +{ + /* Same story as the other two */ + return 0; +} + +DECLARE_TRANSPORT_OPS(hust) = { + .init = init_unix, + .event = transport_unix_dispatch, + .fini = 0, + .put_events = hust_put_events, + .set_events = hust_set_events, + .clear_events = hust_clear_events, +}; + diff --git a/include/hiomapd-socket.h b/include/hiomapd-socket.h new file mode 100644 index 0000000000000000000000000000000000000000..e4183c59ae5cc6b397fc93183b8bb7efa0f60144 --- /dev/null +++ b/include/hiomapd-socket.h @@ -0,0 +1,100 @@ +#ifndef HIOMAPD_SOCKET_H +#define HIOMAPD_SOCKET_H + +#include +/* hiomapd unix socket cmd->husc + * hiomapd unix socket transport->hust + * some of these are redundant or won't ever + * be implemented, but they are hear to mark was the only interface + * provided*/ +enum { + husc_invalid = 0, + /* This is an invalid message, it is only used on responses */ + husx_response = 1, + /* control functions */ + husc_ping, + husc_reset, + husc_kill, + husc_markflashmodified, + husc_suspend, + husc_resume, + husc_setbackend, + husc_daemonstate, + husc_lpcstate, + + /* transport functions */ + hust_reset, + hust_getinfo, + hust_getflashinfo, + hust_createreadwindow, + hust_createwritewindow, + hust_closewindow, + hust_markdirty, + hust_flush, + hust_ack, + hust_erase, + /*v3 options? */ + hust_getflashname, + hust_lock, + /* Properties... + * It may be simpler to just emita properties byte + * over the socket interface + */ + hust_flashcontrollost, + hust_daemonready, + hust_protocolreset, + hust_windowreset, + + /* end of list marker */ + husx_api_limit +}; + +/* + * Each request type will use a different number of args. + * argument usage and placement is request defined. + * + */ +struct unix_hiomapd_request { + uint32_t request; + uint32_t args[7]; +}; + +/* request info blocks */ +/* arguments who's type is smaller than uint32_t will be truncated down to size + * so for example: a uin16_t in argument zero would only use the bottom 16 bit, + * the next argument will be in arg1. + * also note: these are all native endian encoded. + * + * responses: the first response argument is the status byte, + * the second is the number of additional arguments (extranious?) + */ + +/* hust_createreadwindow + * + * Takes two arguments: + * arg[0]: window offset (uint16_t) + * arg[1]: window size (uint16_t) + * + * TODO: how exactly does hiomapd use these values. + * It looks like they are in units of 'page size' + * + */ + +/* hust_createwritewindow + * + * Takes two arguments: + * arg[0]: window offset (uint16_t) + * arg[1]: window size (uint16_t) + * + * TODO: how exactly does hiomapd use these values. + * It looks like they are in units of 'page size' + * + */ + +/* hust_ack + * + * Take on argument: + * arg[0]: flags + * + */ +#endif diff --git a/mboxd.h b/mboxd.h index 5c786fc9e66db3fe0226cac126faaf50261470d9..49283ed70c4fd21a68226180f8d4604d39127acb 100644 --- a/mboxd.h +++ b/mboxd.h @@ -44,7 +44,6 @@ enum api_version { BMC_EVENT_FLASH_CTRL_LOST | \ BMC_EVENT_DAEMON_READY) - #define MAPS_FLASH (1 << 0) #define MAPS_MEM (1 << 1) #define STATE_SUSPENDED (1 << 7) diff --git a/protocol.c b/protocol.c index 955e8370829515234df921a458c4f05efee16489..b131089feb975505f625d39f2729c0f14a0aa151 100644 --- a/protocol.c +++ b/protocol.c @@ -62,7 +62,7 @@ int protocol_events_set(struct mbox_context *context, uint8_t bmc_event) context->bmc_events |= bmc_event; return (context->transport) ? - context->transport->set_events(context, bmc_event, mask) : + context->transport->ops->set_events(context, bmc_event, mask) : (mbox_log(LOG_ERR, "Called %s, but transport not assigned", __func__), -1); }