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