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