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