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 75struct 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 82static MALLOC_DEFINE(M_IPMI, "ipmi", "OPAL IPMI"); 83 84static int 85opal_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 126static void 127opal_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 147static int 148opal_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 206out: 207 return (err); 208} 209 210static int 211opal_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 221static void 222opal_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 245static int 246opal_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 253static int 254opal_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 273static int 274opal_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 302static int 303opal_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 316static 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 324static driver_t opal_ipmi_driver = { 325 "ipmi", 326 opal_ipmi_methods, 327 sizeof(struct opal_ipmi_softc) 328}; 329 330DRIVER_MODULE(opal_ipmi, opal, opal_ipmi_driver, ipmi_devclass, NULL, NULL); 331