The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/ipmi/ipmi_opal.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (C) 2018 Justin Hibbits
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer.
    9  * 2. Redistributions in binary form must reproduce the above copyright
   10  *    notice, this list of conditions and the following disclaimer in the
   11  *    documentation and/or other materials provided with the distribution.
   12  *
   13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   16  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   17  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   18  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
   19  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   20  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   21  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
   22  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   23  */
   24 
   25 #include <sys/cdefs.h>
   26 __FBSDID("$FreeBSD$");
   27 
   28 #include <sys/param.h>
   29 #include <sys/kernel.h>
   30 #include <sys/systm.h>
   31 #include <sys/lock.h>
   32 #include <sys/module.h>
   33 #include <sys/mutex.h>
   34 #include <sys/bus.h>
   35 #include <sys/kthread.h>
   36 #include <sys/proc.h>
   37 #include <sys/selinfo.h>
   38 #include <sys/sysctl.h>
   39 
   40 #include <vm/vm.h>
   41 #include <vm/pmap.h>
   42 
   43 #include <machine/bus.h>
   44 
   45 #include <dev/ofw/openfirm.h>
   46 #include <dev/ofw/ofw_bus.h>
   47 #include <dev/ofw/ofw_bus_subr.h>
   48 
   49 #include <sys/ipmi.h>
   50 #include <dev/ipmi/ipmivars.h>
   51 
   52 #include <powerpc/powernv/opal.h>
   53 
   54 /*
   55  * OPAL_IPMI_DEBUG
   56  *
   57  * 0 - disabled
   58  * 1 - enable error messages (EPRINTF)
   59  * 2 - enable error and debug messages (DPRINTF)
   60  */
   61 #define OPAL_IPMI_DEBUG         0
   62 #if OPAL_IPMI_DEBUG >= 2
   63 /* debug printf */
   64 #define DPRINTF(fmt, ...)       printf("ipmi: " fmt "\n", ## __VA_ARGS__)
   65 #else
   66 #define DPRINTF(fmt, ...)       ((void)0)
   67 #endif
   68 #if OPAL_IPMI_DEBUG >= 1
   69 /* error printf: to print messages only when something fails */
   70 #define EPRINTF(fmt, ...)       printf("ipmi: " fmt "\n", ## __VA_ARGS__)
   71 #else
   72 #define EPRINTF(fmt, ...)       ((void)0)
   73 #endif
   74 
   75 struct opal_ipmi_softc {
   76         struct ipmi_softc ipmi;
   77         uint64_t sc_interface;
   78         int sc_timedout;
   79         struct opal_ipmi_msg *sc_msg; /* Protected by IPMI lock */
   80 };
   81 
   82 static MALLOC_DEFINE(M_IPMI, "ipmi", "OPAL IPMI");
   83 
   84 static int
   85 opal_ipmi_recv(struct opal_ipmi_softc *sc, uint64_t *msg_len, int timo)
   86 {
   87         int err;
   88 
   89         if (timo == 0)
   90                 timo = MAX_TIMEOUT;
   91         timo *= 10; /* Timeout is in milliseconds, we delay in 100us */
   92 
   93         for (;;) {
   94                 *msg_len = sizeof(struct opal_ipmi_msg) + IPMI_MAX_RX;
   95                 /* Crank the OPAL state machine while we poll for a reply. */
   96                 opal_call(OPAL_POLL_EVENTS, NULL);
   97                 err = opal_call(OPAL_IPMI_RECV, sc->sc_interface,
   98                     vtophys(sc->sc_msg), vtophys(msg_len));
   99                 if (err != OPAL_EMPTY)
  100                         break;
  101 
  102                 DELAY(100);
  103                 if (timo-- <= 0) {
  104                         sc->sc_timedout = 1;
  105                         break;
  106                 }
  107         }
  108 
  109         if (err != OPAL_SUCCESS)
  110                 EPRINTF("RECV: error: %d", err);
  111 
  112         switch (err) {
  113         case OPAL_SUCCESS:
  114                 DPRINTF("RECV: rv=%02x len=%ld",
  115                     sc->sc_msg->data[0], *msg_len);
  116                 return (0);
  117         case OPAL_RESOURCE:
  118                 return (ENOMEM);
  119         case OPAL_EMPTY:
  120                 return (EAGAIN);
  121         default:
  122                 return (EIO);
  123         }
  124 }
  125 
  126 static void
  127 opal_ipmi_discard_msgs(struct opal_ipmi_softc *sc)
  128 {
  129         uint64_t msg_len;
  130         int err, i = 0;
  131 
  132         /* OPAL_IPMI_RECV fails when msg version is not set. */
  133         sc->sc_msg->version = OPAL_IPMI_MSG_FORMAT_VERSION_1;
  134 
  135         /* Wait up to 100ms for the 1st timedout message. */
  136         err = opal_ipmi_recv(sc, &msg_len, 100);
  137         while (err == 0) {
  138                 i++;
  139                 /* Wait only 10ms for the remaining messages. */
  140                 err = opal_ipmi_recv(sc, &msg_len, 10);
  141         }
  142         if (i > 0)
  143                 EPRINTF("Discarded %d message(s)", i);
  144         sc->sc_timedout = 0;
  145 }
  146 
  147 static int
  148 opal_ipmi_polled_request(struct opal_ipmi_softc *sc, struct ipmi_request *req,
  149     int timo)
  150 {
  151         uint64_t msg_len;
  152         int err;
  153 
  154         /*
  155          * Discard timed out messages before sending a new one, to avoid
  156          * them being confused with the reply of the new message.
  157          */
  158         if (sc->sc_timedout)
  159                 opal_ipmi_discard_msgs(sc);
  160 
  161         /* Construct and send the message. */
  162         sc->sc_msg->version = OPAL_IPMI_MSG_FORMAT_VERSION_1;
  163         sc->sc_msg->netfn = req->ir_addr;
  164         sc->sc_msg->cmd = req->ir_command;
  165 
  166         if (req->ir_requestlen > IPMI_MAX_RX) {
  167                 err = ENOMEM;
  168                 goto out;
  169         }
  170         memcpy(sc->sc_msg->data, req->ir_request, req->ir_requestlen);
  171 
  172         msg_len = sizeof(*sc->sc_msg) + req->ir_requestlen;
  173         err = opal_call(OPAL_IPMI_SEND, sc->sc_interface, vtophys(sc->sc_msg),
  174             msg_len);
  175 
  176         DPRINTF("SEND: cmd=%02x netfn=%02x len=%ld -> %d",
  177             sc->sc_msg->cmd, sc->sc_msg->netfn, msg_len, err);
  178 
  179         if (err != OPAL_SUCCESS)
  180                 EPRINTF("SEND: error: %d", err);
  181 
  182         switch (err) {
  183         case OPAL_SUCCESS:
  184                 break;
  185         case OPAL_PARAMETER:
  186         case OPAL_UNSUPPORTED:
  187                 err = EINVAL;
  188                 goto out;
  189         case OPAL_RESOURCE:
  190                 err = ENOMEM;
  191                 goto out;
  192         case OPAL_HARDWARE:
  193         default:
  194                 err = EIO;
  195                 goto out;
  196         }
  197 
  198         if ((err = opal_ipmi_recv(sc, &msg_len, timo)) == 0) {
  199                 /* Subtract one extra for the completion code. */
  200                 req->ir_replylen = msg_len - sizeof(struct opal_ipmi_msg) - 1;
  201                 req->ir_replylen = min(req->ir_replylen, req->ir_replybuflen);
  202                 memcpy(req->ir_reply, &sc->sc_msg->data[1], req->ir_replylen);
  203                 req->ir_compcode = sc->sc_msg->data[0];
  204         }
  205 
  206 out:
  207         return (err);
  208 }
  209 
  210 static int
  211 opal_ipmi_probe(device_t dev)
  212 {
  213         if (!ofw_bus_is_compatible(dev, "ibm,opal-ipmi"))
  214                 return (ENXIO);
  215 
  216         device_set_desc(dev, "OPAL IPMI System Interface");
  217 
  218         return (BUS_PROBE_DEFAULT);
  219 }
  220 
  221 static void
  222 opal_ipmi_loop(void *arg)
  223 {
  224         struct opal_ipmi_softc *sc = arg;
  225         struct ipmi_request *req;
  226         int i, err;
  227 
  228         IPMI_LOCK(&sc->ipmi);
  229         while ((req = ipmi_dequeue_request(&sc->ipmi)) != NULL) {
  230                 IPMI_UNLOCK(&sc->ipmi);
  231                 err = EIO;
  232                 for (i = 0; i < 3 && err != 0; i++) {
  233                         IPMI_IO_LOCK(&sc->ipmi);
  234                         err = opal_ipmi_polled_request(sc, req, MAX_TIMEOUT);
  235                         IPMI_IO_UNLOCK(&sc->ipmi);
  236                 }
  237                 req->ir_error = err == 0 ? 0 : EIO;
  238                 IPMI_LOCK(&sc->ipmi);
  239                 ipmi_complete_request(&sc->ipmi, req);
  240         }
  241         IPMI_UNLOCK(&sc->ipmi);
  242         kproc_exit(0);
  243 }
  244 
  245 static int
  246 opal_ipmi_startup(struct ipmi_softc *sc)
  247 {
  248 
  249         return (kproc_create(opal_ipmi_loop, sc, &sc->ipmi_kthread, 0, 0,
  250             "%s: opal", device_get_nameunit(sc->ipmi_dev)));
  251 }
  252 
  253 static int
  254 opal_ipmi_driver_request(struct ipmi_softc *isc, struct ipmi_request *req,
  255     int timo)
  256 {
  257         struct opal_ipmi_softc *sc = (struct opal_ipmi_softc *)isc;
  258         int i, err;
  259 
  260         for (i = 0; i < 3; i++) {
  261                 IPMI_LOCK(&sc->ipmi);
  262                 err = opal_ipmi_polled_request(sc, req, timo);
  263                 IPMI_UNLOCK(&sc->ipmi);
  264                 if (err == 0)
  265                         break;
  266         }
  267 
  268         req->ir_error = err;
  269 
  270         return (err);
  271 }
  272 
  273 static int
  274 opal_ipmi_attach(device_t dev)
  275 {
  276         struct opal_ipmi_softc *sc;
  277         pcell_t ifid;
  278 
  279         sc = device_get_softc(dev);
  280 
  281         if (OF_getencprop(ofw_bus_get_node(dev), "ibm,ipmi-interface-id",
  282             &ifid, sizeof(ifid)) < 0) {
  283                 device_printf(dev, "Missing interface id\n");
  284                 return (ENXIO);
  285         }
  286         sc->sc_interface = ifid;
  287         sc->ipmi.ipmi_startup = opal_ipmi_startup;
  288         sc->ipmi.ipmi_driver_request = opal_ipmi_driver_request;
  289         sc->ipmi.ipmi_enqueue_request = ipmi_polled_enqueue_request;
  290         sc->ipmi.ipmi_driver_requests_polled = 1;
  291         sc->ipmi.ipmi_dev = dev;
  292 
  293         sc->sc_msg = malloc(sizeof(struct opal_ipmi_msg) + IPMI_MAX_RX, M_IPMI,
  294             M_WAITOK | M_ZERO);
  295 
  296         /* Discard old messages that may have remained in receive queue. */
  297         opal_ipmi_discard_msgs(sc);
  298 
  299         return (ipmi_attach(dev));
  300 }
  301 
  302 static int
  303 opal_ipmi_detach(device_t dev)
  304 {
  305         struct opal_ipmi_softc *sc;
  306         int err;
  307 
  308         sc = device_get_softc(dev);
  309         err = ipmi_detach(dev);
  310         if (err == 0)
  311                 free(sc->sc_msg, M_IPMI);
  312 
  313         return (err);
  314 }
  315 
  316 static device_method_t  opal_ipmi_methods[] = {
  317         /* Device interface */
  318         DEVMETHOD(device_probe,         opal_ipmi_probe),
  319         DEVMETHOD(device_attach,        opal_ipmi_attach),
  320         DEVMETHOD(device_detach,        opal_ipmi_detach),
  321         DEVMETHOD_END
  322 };
  323 
  324 static driver_t opal_ipmi_driver = {
  325         "ipmi",
  326         opal_ipmi_methods,
  327         sizeof(struct opal_ipmi_softc)
  328 };
  329 
  330 DRIVER_MODULE(opal_ipmi, opal, opal_ipmi_driver, NULL, NULL);

Cache object: 780dfc2cd6634fe50f9529cffa440097


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.