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 #include <sys/param.h> 27 #include <sys/kernel.h> 28 #include <sys/systm.h> 29 #include <sys/lock.h> 30 #include <sys/module.h> 31 #include <sys/mutex.h> 32 #include <sys/bus.h> 33 #include <sys/kthread.h> 34 #include <sys/proc.h> 35 #include <sys/selinfo.h> 36 #include <sys/sysctl.h> 37 38 #include <vm/vm.h> 39 #include <vm/pmap.h> 40 41 #include <machine/bus.h> 42 43 #include <dev/ofw/openfirm.h> 44 #include <dev/ofw/ofw_bus.h> 45 #include <dev/ofw/ofw_bus_subr.h> 46 47 #include <sys/ipmi.h> 48 #include <dev/ipmi/ipmivars.h> 49 50 #include <powerpc/powernv/opal.h> 51 52 /* 53 * OPAL_IPMI_DEBUG 54 * 55 * 0 - disabled 56 * 1 - enable error messages (EPRINTF) 57 * 2 - enable error and debug messages (DPRINTF) 58 */ 59 #define OPAL_IPMI_DEBUG 0 60 #if OPAL_IPMI_DEBUG >= 2 61 /* debug printf */ 62 #define DPRINTF(fmt, ...) printf("ipmi: " fmt "\n", ## __VA_ARGS__) 63 #else 64 #define DPRINTF(fmt, ...) ((void)0) 65 #endif 66 #if OPAL_IPMI_DEBUG >= 1 67 /* error printf: to print messages only when something fails */ 68 #define EPRINTF(fmt, ...) printf("ipmi: " fmt "\n", ## __VA_ARGS__) 69 #else 70 #define EPRINTF(fmt, ...) ((void)0) 71 #endif 72 73 struct opal_ipmi_softc { 74 struct ipmi_softc ipmi; 75 uint64_t sc_interface; 76 int sc_timedout; 77 struct opal_ipmi_msg *sc_msg; /* Protected by IPMI lock */ 78 }; 79 80 static MALLOC_DEFINE(M_IPMI, "ipmi", "OPAL IPMI"); 81 82 static int 83 opal_ipmi_recv(struct opal_ipmi_softc *sc, uint64_t *msg_len, int timo) 84 { 85 int err; 86 87 if (timo == 0) 88 timo = MAX_TIMEOUT; 89 timo *= 10; /* Timeout is in milliseconds, we delay in 100us */ 90 91 for (;;) { 92 *msg_len = sizeof(struct opal_ipmi_msg) + IPMI_MAX_RX; 93 /* Crank the OPAL state machine while we poll for a reply. */ 94 opal_call(OPAL_POLL_EVENTS, NULL); 95 err = opal_call(OPAL_IPMI_RECV, sc->sc_interface, 96 vtophys(sc->sc_msg), vtophys(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 int timo) 254 { 255 struct opal_ipmi_softc *sc = (struct opal_ipmi_softc *)isc; 256 int i, err; 257 258 for (i = 0; i < 3; i++) { 259 IPMI_LOCK(&sc->ipmi); 260 err = opal_ipmi_polled_request(sc, req, timo); 261 IPMI_UNLOCK(&sc->ipmi); 262 if (err == 0) 263 break; 264 } 265 266 req->ir_error = err; 267 268 return (err); 269 } 270 271 static int 272 opal_ipmi_attach(device_t dev) 273 { 274 struct opal_ipmi_softc *sc; 275 pcell_t ifid; 276 277 sc = device_get_softc(dev); 278 279 if (OF_getencprop(ofw_bus_get_node(dev), "ibm,ipmi-interface-id", 280 &ifid, sizeof(ifid)) < 0) { 281 device_printf(dev, "Missing interface id\n"); 282 return (ENXIO); 283 } 284 sc->sc_interface = ifid; 285 sc->ipmi.ipmi_startup = opal_ipmi_startup; 286 sc->ipmi.ipmi_driver_request = opal_ipmi_driver_request; 287 sc->ipmi.ipmi_enqueue_request = ipmi_polled_enqueue_request; 288 sc->ipmi.ipmi_driver_requests_polled = 1; 289 sc->ipmi.ipmi_dev = dev; 290 291 sc->sc_msg = malloc(sizeof(struct opal_ipmi_msg) + IPMI_MAX_RX, M_IPMI, 292 M_WAITOK | M_ZERO); 293 294 /* Discard old messages that may have remained in receive queue. */ 295 opal_ipmi_discard_msgs(sc); 296 297 return (ipmi_attach(dev)); 298 } 299 300 static int 301 opal_ipmi_detach(device_t dev) 302 { 303 struct opal_ipmi_softc *sc; 304 int err; 305 306 sc = device_get_softc(dev); 307 err = ipmi_detach(dev); 308 if (err == 0) 309 free(sc->sc_msg, M_IPMI); 310 311 return (err); 312 } 313 314 static device_method_t opal_ipmi_methods[] = { 315 /* Device interface */ 316 DEVMETHOD(device_probe, opal_ipmi_probe), 317 DEVMETHOD(device_attach, opal_ipmi_attach), 318 DEVMETHOD(device_detach, opal_ipmi_detach), 319 DEVMETHOD_END 320 }; 321 322 static driver_t opal_ipmi_driver = { 323 "ipmi", 324 opal_ipmi_methods, 325 sizeof(struct opal_ipmi_softc) 326 }; 327 328 DRIVER_MODULE(opal_ipmi, opal, opal_ipmi_driver, NULL, NULL); 329