From 0712c8c20c98e3256c9a8ff002cd3c06636218fb Mon Sep 17 00:00:00 2001 From: jhibbits Date: Tue, 22 May 2018 03:57:32 +0000 Subject: [PATCH] Add an IPMI attachment for PowerNV systems IPMI access on PowerNV systems is done through the OPAL firmware. This adds a simple attachment for communicating with the FSP/BMC on these machines. This has been tested on a Talos POWER9 workstation, only in the bootup phase, noting the successful attachment messages: ... ipmi0: IPMI device rev. 0, firmware rev. 2.00, version 2.0, device support mask 0 ipmi0: Number of channels 2 ... The ipmi device has not been added to GENERIC64, but may be after further testing. It may also eventually be added to the ipmi module at that point. --- sys/conf/files.powerpc | 2 + sys/powerpc/powernv/opal.h | 15 ++ sys/powerpc/powernv/opal_ipmi.c | 241 ++++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+) create mode 100644 sys/powerpc/powernv/opal_ipmi.c diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc index 1871158c67fb..c379176346d7 100644 --- a/sys/conf/files.powerpc +++ b/sys/conf/files.powerpc @@ -42,6 +42,7 @@ dev/iicbus/ds1631.c optional ds1631 powermac dev/iicbus/ds1775.c optional ds1775 powermac dev/iicbus/max6690.c optional max6690 powermac dev/iicbus/ofw_iicbus.c optional iicbus aim +dev/ipmi/ipmi.c optional ipmi dev/nand/nfc_fsl.c optional nand mpc85xx dev/nand/nfc_rb.c optional nand mpc85xx # Most ofw stuff below is brought in by conf/files for options FDT, but @@ -188,6 +189,7 @@ powerpc/powernv/opal_console.c optional powernv powerpc/powernv/opal_dev.c optional powernv powerpc/powernv/opal_i2c.c optional iicbus fdt powernv powerpc/powernv/opal_i2cm.c optional iicbus fdt powernv +powerpc/powernv/opal_ipmi.c optional powernv ipmi powerpc/powernv/opal_pci.c optional powernv pci powerpc/powernv/opal_sensor.c optional powernv powerpc/powernv/opalcall.S optional powernv diff --git a/sys/powerpc/powernv/opal.h b/sys/powerpc/powernv/opal.h index 2ed8026d76c5..fafeec390e68 100644 --- a/sys/powerpc/powernv/opal.h +++ b/sys/powerpc/powernv/opal.h @@ -73,6 +73,8 @@ int opal_call(uint64_t token, ...); #define OPAL_REINIT_CPUS 70 #define OPAL_CHECK_ASYNC_COMPLETION 86 #define OPAL_SENSOR_READ 88 +#define OPAL_IPMI_SEND 107 +#define OPAL_IPMI_RECV 108 #define OPAL_I2C_REQUEST 109 #define OPAL_INT_GET_XIRR 122 #define OPAL_INT_SET_CPPR 123 @@ -106,8 +108,12 @@ int opal_call(uint64_t token, ...); #define OPAL_PARAMETER -1 #define OPAL_BUSY -2 #define OPAL_CLOSED -5 +#define OPAL_HARDWARE -6 +#define OPAL_UNSUPPORTED -7 +#define OPAL_RESOURCE -10 #define OPAL_BUSY_EVENT -12 #define OPAL_ASYNC_COMPLETION -15 +#define OPAL_EMPTY -16 struct opal_msg { uint32_t msg_type; @@ -127,4 +133,13 @@ enum opal_msg_type { OPAL_MSG_TYPE_MAX, }; +#define OPAL_IPMI_MSG_FORMAT_VERSION_1 1 + +struct opal_ipmi_msg { + uint8_t version; + uint8_t netfn; + uint8_t cmd; + uint8_t data[]; +}; + #endif diff --git a/sys/powerpc/powernv/opal_ipmi.c b/sys/powerpc/powernv/opal_ipmi.c new file mode 100644 index 000000000000..051822d48712 --- /dev/null +++ b/sys/powerpc/powernv/opal_ipmi.c @@ -0,0 +1,241 @@ +/*- + * Copyright (C) 2018 Justin Hibbits + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include "opal.h" + +struct opal_ipmi_softc { + struct ipmi_softc ipmi; + uint64_t sc_interface; + struct opal_ipmi_msg *sc_msg; /* Protected by IPMI lock */ +}; + +static MALLOC_DEFINE(M_IPMI, "ipmi", "OPAL IPMI"); + +static int +opal_ipmi_polled_request(struct opal_ipmi_softc *sc, struct ipmi_request *req, + int timo) +{ + uint64_t msg_len; + int err; + + /* Construct and send the message. */ + sc->sc_msg->version = OPAL_IPMI_MSG_FORMAT_VERSION_1; + sc->sc_msg->netfn = req->ir_addr; + sc->sc_msg->cmd = req->ir_command; + + if (req->ir_requestlen > IPMI_MAX_RX) { + err = ENOMEM; + goto out; + } + memcpy(sc->sc_msg->data, req->ir_request, req->ir_requestlen); + + msg_len = sizeof(*sc->sc_msg) + req->ir_requestlen; + err = opal_call(OPAL_IPMI_SEND, sc->sc_interface, vtophys(sc->sc_msg), + msg_len); + switch (err) { + case OPAL_SUCCESS: + break; + case OPAL_PARAMETER: + err = EINVAL; + goto out; + case OPAL_HARDWARE: + err = EIO; + goto out; + case OPAL_UNSUPPORTED: + err = EINVAL; + goto out; + case OPAL_RESOURCE: + err = ENOMEM; + goto out; + } + + timo *= 10; /* Timeout is in milliseconds, we delay in 100us */ + do { + msg_len = sizeof(struct opal_ipmi_msg) + IPMI_MAX_RX; + err = opal_call(OPAL_IPMI_RECV, sc->sc_interface, + vtophys(sc->sc_msg), vtophys(&msg_len)); + if (err != OPAL_EMPTY) + break; + DELAY(100); + } while (err == OPAL_EMPTY && timo-- != 0); + + switch (err) { + case OPAL_SUCCESS: + /* Subtract one extra for the completion code. */ + req->ir_replylen = msg_len - sizeof(struct opal_ipmi_msg) - 1; + req->ir_replylen = min(req->ir_replylen, req->ir_replybuflen); + memcpy(req->ir_reply, &sc->sc_msg->data[1], req->ir_replylen); + req->ir_compcode = sc->sc_msg->data[0]; + break; + case OPAL_RESOURCE: + err = ENOMEM; + break; + case OPAL_EMPTY: + err = EAGAIN; + break; + default: + err = EIO; + break; + } + +out: + + return (err); +} + +static int +opal_ipmi_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "ibm,opal-ipmi")) + return (ENXIO); + + device_set_desc(dev, "OPAL IPMI System Interface"); + + return (BUS_PROBE_DEFAULT); +} + +static void +opal_ipmi_loop(void *arg) +{ + struct opal_ipmi_softc *sc = arg; + struct ipmi_request *req; + int i, ok; + + IPMI_LOCK(&sc->ipmi); + while ((req = ipmi_dequeue_request(&sc->ipmi)) != NULL) { + IPMI_UNLOCK(&sc->ipmi); + ok = 0; + for (i = 0; i < 3 && !ok; i++) { + IPMI_IO_LOCK(&sc->ipmi); + ok = opal_ipmi_polled_request(sc, req, MAX_TIMEOUT); + IPMI_IO_UNLOCK(&sc->ipmi); + } + if (ok) + req->ir_error = 0; + else + req->ir_error = EIO; + IPMI_LOCK(&sc->ipmi); + ipmi_complete_request(&sc->ipmi, req); + } + IPMI_UNLOCK(&sc->ipmi); + kproc_exit(0); +} + +static int +opal_ipmi_startup(struct ipmi_softc *sc) +{ + + return (kproc_create(opal_ipmi_loop, sc, &sc->ipmi_kthread, 0, 0, + "%s: opal", device_get_nameunit(sc->ipmi_dev))); +} + +static int +opal_ipmi_driver_request(struct ipmi_softc *isc, struct ipmi_request *req, + int timo) +{ + struct opal_ipmi_softc *sc = (struct opal_ipmi_softc *)isc; + int i, err; + + for (i = 0; i < 3; i++) { + IPMI_LOCK(&sc->ipmi); + err = opal_ipmi_polled_request(sc, req, timo); + IPMI_UNLOCK(&sc->ipmi); + if (err == 0) + break; + } + + req->ir_error = err; + + return (err); +} + +static int +opal_ipmi_attach(device_t dev) +{ + struct opal_ipmi_softc *sc; + + sc = device_get_softc(dev); + + if (OF_getencprop(ofw_bus_get_node(dev), "ibm,ipmi-interface-id", + (pcell_t*)&sc->sc_interface, sizeof(sc->sc_interface)) < 0) { + device_printf(dev, "Missing interface id\n"); + return (ENXIO); + } + sc->ipmi.ipmi_startup = opal_ipmi_startup; + sc->ipmi.ipmi_driver_request = opal_ipmi_driver_request; + sc->ipmi.ipmi_dev = dev; + + sc->sc_msg = malloc(sizeof(struct opal_ipmi_msg) + IPMI_MAX_RX, M_IPMI, + M_WAITOK | M_ZERO); + + return (ipmi_attach(dev)); +} + +static int +opal_ipmi_detach(device_t dev) +{ + return (EBUSY); +} + +static device_method_t opal_ipmi_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, opal_ipmi_probe), + DEVMETHOD(device_attach, opal_ipmi_attach), + DEVMETHOD(device_detach, opal_ipmi_detach), + DEVMETHOD_END +}; + +static driver_t opal_ipmi_driver = { + "ipmi", + opal_ipmi_methods, + sizeof(struct opal_ipmi_softc) +}; + +DRIVER_MODULE(opal_ipmi, opal, opal_ipmi_driver, ipmi_devclass, NULL, NULL);