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 struct opal_ipmi_softc { 55 struct ipmi_softc ipmi; 56 uint64_t sc_interface; 57 struct opal_ipmi_msg *sc_msg; /* Protected by IPMI lock */ 58 }; 59 60 static MALLOC_DEFINE(M_IPMI, "ipmi", "OPAL IPMI"); 61 62 static int 63 opal_ipmi_polled_request(struct opal_ipmi_softc *sc, struct ipmi_request *req, 64 int timo) 65 { 66 uint64_t msg_len; 67 int err; 68 69 /* Construct and send the message. */ 70 sc->sc_msg->version = OPAL_IPMI_MSG_FORMAT_VERSION_1; 71 sc->sc_msg->netfn = req->ir_addr; 72 sc->sc_msg->cmd = req->ir_command; 73 74 if (req->ir_requestlen > IPMI_MAX_RX) { 75 err = ENOMEM; 76 goto out; 77 } 78 memcpy(sc->sc_msg->data, req->ir_request, req->ir_requestlen); 79 80 msg_len = sizeof(*sc->sc_msg) + req->ir_requestlen; 81 err = opal_call(OPAL_IPMI_SEND, sc->sc_interface, vtophys(sc->sc_msg), 82 msg_len); 83 switch (err) { 84 case OPAL_SUCCESS: 85 break; 86 case OPAL_PARAMETER: 87 err = EINVAL; 88 goto out; 89 case OPAL_HARDWARE: 90 err = EIO; 91 goto out; 92 case OPAL_UNSUPPORTED: 93 err = EINVAL; 94 goto out; 95 case OPAL_RESOURCE: 96 err = ENOMEM; 97 goto out; 98 } 99 100 timo *= 10; /* Timeout is in milliseconds, we delay in 100us */ 101 do { 102 msg_len = sizeof(struct opal_ipmi_msg) + IPMI_MAX_RX; 103 /* Crank the OPAL state machine while we poll for a reply. */ 104 opal_call(OPAL_POLL_EVENTS, NULL); 105 err = opal_call(OPAL_IPMI_RECV, sc->sc_interface, 106 vtophys(sc->sc_msg), vtophys(&msg_len)); 107 if (err != OPAL_EMPTY) 108 break; 109 DELAY(100); 110 } while (err == OPAL_EMPTY && timo-- != 0); 111 112 switch (err) { 113 case OPAL_SUCCESS: 114 /* Subtract one extra for the completion code. */ 115 req->ir_replylen = msg_len - sizeof(struct opal_ipmi_msg) - 1; 116 req->ir_replylen = min(req->ir_replylen, req->ir_replybuflen); 117 memcpy(req->ir_reply, &sc->sc_msg->data[1], req->ir_replylen); 118 req->ir_compcode = sc->sc_msg->data[0]; 119 err = 0; 120 break; 121 case OPAL_RESOURCE: 122 err = ENOMEM; 123 break; 124 case OPAL_EMPTY: 125 err = EAGAIN; 126 break; 127 default: 128 err = EIO; 129 break; 130 } 131 132 out: 133 134 return (err); 135 } 136 137 static int 138 opal_ipmi_probe(device_t dev) 139 { 140 if (!ofw_bus_is_compatible(dev, "ibm,opal-ipmi")) 141 return (ENXIO); 142 143 device_set_desc(dev, "OPAL IPMI System Interface"); 144 145 return (BUS_PROBE_DEFAULT); 146 } 147 148 static void 149 opal_ipmi_loop(void *arg) 150 { 151 struct opal_ipmi_softc *sc = arg; 152 struct ipmi_request *req; 153 int i, ok; 154 155 IPMI_LOCK(&sc->ipmi); 156 while ((req = ipmi_dequeue_request(&sc->ipmi)) != NULL) { 157 IPMI_UNLOCK(&sc->ipmi); 158 ok = 0; 159 for (i = 0; i < 3 && !ok; i++) { 160 IPMI_IO_LOCK(&sc->ipmi); 161 ok = opal_ipmi_polled_request(sc, req, MAX_TIMEOUT); 162 IPMI_IO_UNLOCK(&sc->ipmi); 163 } 164 if (ok) 165 req->ir_error = 0; 166 else 167 req->ir_error = EIO; 168 IPMI_LOCK(&sc->ipmi); 169 ipmi_complete_request(&sc->ipmi, req); 170 } 171 IPMI_UNLOCK(&sc->ipmi); 172 kproc_exit(0); 173 } 174 175 static int 176 opal_ipmi_startup(struct ipmi_softc *sc) 177 { 178 179 return (kproc_create(opal_ipmi_loop, sc, &sc->ipmi_kthread, 0, 0, 180 "%s: opal", device_get_nameunit(sc->ipmi_dev))); 181 } 182 183 static int 184 opal_ipmi_driver_request(struct ipmi_softc *isc, struct ipmi_request *req, 185 int timo) 186 { 187 struct opal_ipmi_softc *sc = (struct opal_ipmi_softc *)isc; 188 int i, err; 189 190 for (i = 0; i < 3; i++) { 191 IPMI_LOCK(&sc->ipmi); 192 err = opal_ipmi_polled_request(sc, req, timo); 193 IPMI_UNLOCK(&sc->ipmi); 194 if (err == 0) 195 break; 196 } 197 198 req->ir_error = err; 199 200 return (err); 201 } 202 203 static int 204 opal_ipmi_attach(device_t dev) 205 { 206 struct opal_ipmi_softc *sc; 207 208 sc = device_get_softc(dev); 209 210 if (OF_getencprop(ofw_bus_get_node(dev), "ibm,ipmi-interface-id", 211 (pcell_t*)&sc->sc_interface, sizeof(sc->sc_interface)) < 0) { 212 device_printf(dev, "Missing interface id\n"); 213 return (ENXIO); 214 } 215 sc->ipmi.ipmi_startup = opal_ipmi_startup; 216 sc->ipmi.ipmi_driver_request = opal_ipmi_driver_request; 217 sc->ipmi.ipmi_enqueue_request = ipmi_polled_enqueue_request; 218 sc->ipmi.ipmi_driver_requests_polled = 1; 219 sc->ipmi.ipmi_dev = dev; 220 221 sc->sc_msg = malloc(sizeof(struct opal_ipmi_msg) + IPMI_MAX_RX, M_IPMI, 222 M_WAITOK | M_ZERO); 223 224 return (ipmi_attach(dev)); 225 } 226 227 static int 228 opal_ipmi_detach(device_t dev) 229 { 230 struct opal_ipmi_softc *sc; 231 int err; 232 233 sc = device_get_softc(dev); 234 err = ipmi_detach(dev); 235 if (err == 0) 236 free(sc->sc_msg, M_IPMI); 237 238 return (err); 239 } 240 241 static device_method_t opal_ipmi_methods[] = { 242 /* Device interface */ 243 DEVMETHOD(device_probe, opal_ipmi_probe), 244 DEVMETHOD(device_attach, opal_ipmi_attach), 245 DEVMETHOD(device_detach, opal_ipmi_detach), 246 DEVMETHOD_END 247 }; 248 249 static driver_t opal_ipmi_driver = { 250 "ipmi", 251 opal_ipmi_methods, 252 sizeof(struct opal_ipmi_softc) 253 }; 254 255 DRIVER_MODULE(opal_ipmi, opal, opal_ipmi_driver, ipmi_devclass, NULL, NULL); 256