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 if (err != OPAL_EMPTY) 97 break; 98 99 DELAY(100); 100 if (timo-- <= 0) { 101 sc->sc_timedout = 1; 102 break; 103 } 104 } 105 106 if (err != OPAL_SUCCESS) 107 EPRINTF("RECV: error: %d", err); 108 109 switch (err) { 110 case OPAL_SUCCESS: 111 DPRINTF("RECV: rv=%02x len=%ld", 112 sc->sc_msg->data[0], *msg_len); 113 return (0); 114 case OPAL_RESOURCE: 115 return (ENOMEM); 116 case OPAL_EMPTY: 117 return (EAGAIN); 118 default: 119 return (EIO); 120 } 121 } 122 123 static void 124 opal_ipmi_discard_msgs(struct opal_ipmi_softc *sc) 125 { 126 uint64_t msg_len; 127 int err, i = 0; 128 129 /* OPAL_IPMI_RECV fails when msg version is not set. */ 130 sc->sc_msg->version = OPAL_IPMI_MSG_FORMAT_VERSION_1; 131 132 /* Wait up to 100ms for the 1st timedout message. */ 133 err = opal_ipmi_recv(sc, &msg_len, 100); 134 while (err == 0) { 135 i++; 136 /* Wait only 10ms for the remaining messages. */ 137 err = opal_ipmi_recv(sc, &msg_len, 10); 138 } 139 if (i > 0) 140 EPRINTF("Discarded %d message(s)", i); 141 sc->sc_timedout = 0; 142 } 143 144 static int 145 opal_ipmi_polled_request(struct opal_ipmi_softc *sc, struct ipmi_request *req, 146 int timo) 147 { 148 uint64_t msg_len; 149 int err; 150 151 /* 152 * Discard timed out messages before sending a new one, to avoid 153 * them being confused with the reply of the new message. 154 */ 155 if (sc->sc_timedout) 156 opal_ipmi_discard_msgs(sc); 157 158 /* Construct and send the message. */ 159 sc->sc_msg->version = OPAL_IPMI_MSG_FORMAT_VERSION_1; 160 sc->sc_msg->netfn = req->ir_addr; 161 sc->sc_msg->cmd = req->ir_command; 162 163 if (req->ir_requestlen > IPMI_MAX_RX) { 164 err = ENOMEM; 165 goto out; 166 } 167 memcpy(sc->sc_msg->data, req->ir_request, req->ir_requestlen); 168 169 msg_len = sizeof(*sc->sc_msg) + req->ir_requestlen; 170 err = opal_call(OPAL_IPMI_SEND, sc->sc_interface, vtophys(sc->sc_msg), 171 msg_len); 172 173 DPRINTF("SEND: cmd=%02x netfn=%02x len=%ld -> %d", 174 sc->sc_msg->cmd, sc->sc_msg->netfn, msg_len, err); 175 176 if (err != OPAL_SUCCESS) 177 EPRINTF("SEND: error: %d", err); 178 179 switch (err) { 180 case OPAL_SUCCESS: 181 break; 182 case OPAL_PARAMETER: 183 case OPAL_UNSUPPORTED: 184 err = EINVAL; 185 goto out; 186 case OPAL_RESOURCE: 187 err = ENOMEM; 188 goto out; 189 case OPAL_HARDWARE: 190 default: 191 err = EIO; 192 goto out; 193 } 194 195 if ((err = opal_ipmi_recv(sc, &msg_len, timo)) == 0) { 196 /* Subtract one extra for the completion code. */ 197 req->ir_replylen = msg_len - sizeof(struct opal_ipmi_msg) - 1; 198 req->ir_replylen = min(req->ir_replylen, req->ir_replybuflen); 199 memcpy(req->ir_reply, &sc->sc_msg->data[1], req->ir_replylen); 200 req->ir_compcode = sc->sc_msg->data[0]; 201 } 202 203 out: 204 return (err); 205 } 206 207 static int 208 opal_ipmi_probe(device_t dev) 209 { 210 if (!ofw_bus_is_compatible(dev, "ibm,opal-ipmi")) 211 return (ENXIO); 212 213 device_set_desc(dev, "OPAL IPMI System Interface"); 214 215 return (BUS_PROBE_DEFAULT); 216 } 217 218 static void 219 opal_ipmi_loop(void *arg) 220 { 221 struct opal_ipmi_softc *sc = arg; 222 struct ipmi_request *req; 223 int i, err; 224 225 IPMI_LOCK(&sc->ipmi); 226 while ((req = ipmi_dequeue_request(&sc->ipmi)) != NULL) { 227 IPMI_UNLOCK(&sc->ipmi); 228 err = EIO; 229 for (i = 0; i < 3 && err != 0; i++) { 230 IPMI_IO_LOCK(&sc->ipmi); 231 err = opal_ipmi_polled_request(sc, req, MAX_TIMEOUT); 232 IPMI_IO_UNLOCK(&sc->ipmi); 233 } 234 req->ir_error = err == 0 ? 0 : EIO; 235 IPMI_LOCK(&sc->ipmi); 236 ipmi_complete_request(&sc->ipmi, req); 237 } 238 IPMI_UNLOCK(&sc->ipmi); 239 kproc_exit(0); 240 } 241 242 static int 243 opal_ipmi_startup(struct ipmi_softc *sc) 244 { 245 246 return (kproc_create(opal_ipmi_loop, sc, &sc->ipmi_kthread, 0, 0, 247 "%s: opal", device_get_nameunit(sc->ipmi_dev))); 248 } 249 250 static int 251 opal_ipmi_driver_request(struct ipmi_softc *isc, struct ipmi_request *req, 252 int timo) 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, timo); 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