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 err = opal_call(OPAL_IPMI_RECV, sc->sc_interface, 103 vtophys(sc->sc_msg), vtophys(&msg_len)); 104 if (err != OPAL_EMPTY) 105 break; 106 DELAY(100); 107 } while (err == OPAL_EMPTY && timo-- != 0); 108 109 switch (err) { 110 case OPAL_SUCCESS: 111 /* Subtract one extra for the completion code. */ 112 req->ir_replylen = msg_len - sizeof(struct opal_ipmi_msg) - 1; 113 req->ir_replylen = min(req->ir_replylen, req->ir_replybuflen); 114 memcpy(req->ir_reply, &sc->sc_msg->data[1], req->ir_replylen); 115 req->ir_compcode = sc->sc_msg->data[0]; 116 break; 117 case OPAL_RESOURCE: 118 err = ENOMEM; 119 break; 120 case OPAL_EMPTY: 121 err = EAGAIN; 122 break; 123 default: 124 err = EIO; 125 break; 126 } 127 128 out: 129 130 return (err); 131 } 132 133 static int 134 opal_ipmi_probe(device_t dev) 135 { 136 if (!ofw_bus_is_compatible(dev, "ibm,opal-ipmi")) 137 return (ENXIO); 138 139 device_set_desc(dev, "OPAL IPMI System Interface"); 140 141 return (BUS_PROBE_DEFAULT); 142 } 143 144 static void 145 opal_ipmi_loop(void *arg) 146 { 147 struct opal_ipmi_softc *sc = arg; 148 struct ipmi_request *req; 149 int i, ok; 150 151 IPMI_LOCK(&sc->ipmi); 152 while ((req = ipmi_dequeue_request(&sc->ipmi)) != NULL) { 153 IPMI_UNLOCK(&sc->ipmi); 154 ok = 0; 155 for (i = 0; i < 3 && !ok; i++) { 156 IPMI_IO_LOCK(&sc->ipmi); 157 ok = opal_ipmi_polled_request(sc, req, MAX_TIMEOUT); 158 IPMI_IO_UNLOCK(&sc->ipmi); 159 } 160 if (ok) 161 req->ir_error = 0; 162 else 163 req->ir_error = EIO; 164 IPMI_LOCK(&sc->ipmi); 165 ipmi_complete_request(&sc->ipmi, req); 166 } 167 IPMI_UNLOCK(&sc->ipmi); 168 kproc_exit(0); 169 } 170 171 static int 172 opal_ipmi_startup(struct ipmi_softc *sc) 173 { 174 175 return (kproc_create(opal_ipmi_loop, sc, &sc->ipmi_kthread, 0, 0, 176 "%s: opal", device_get_nameunit(sc->ipmi_dev))); 177 } 178 179 static int 180 opal_ipmi_driver_request(struct ipmi_softc *isc, struct ipmi_request *req, 181 int timo) 182 { 183 struct opal_ipmi_softc *sc = (struct opal_ipmi_softc *)isc; 184 int i, err; 185 186 for (i = 0; i < 3; i++) { 187 IPMI_LOCK(&sc->ipmi); 188 err = opal_ipmi_polled_request(sc, req, timo); 189 IPMI_UNLOCK(&sc->ipmi); 190 if (err == 0) 191 break; 192 } 193 194 req->ir_error = err; 195 196 return (err); 197 } 198 199 static int 200 opal_ipmi_attach(device_t dev) 201 { 202 struct opal_ipmi_softc *sc; 203 204 sc = device_get_softc(dev); 205 206 if (OF_getencprop(ofw_bus_get_node(dev), "ibm,ipmi-interface-id", 207 (pcell_t*)&sc->sc_interface, sizeof(sc->sc_interface)) < 0) { 208 device_printf(dev, "Missing interface id\n"); 209 return (ENXIO); 210 } 211 sc->ipmi.ipmi_startup = opal_ipmi_startup; 212 sc->ipmi.ipmi_driver_request = opal_ipmi_driver_request; 213 sc->ipmi.ipmi_enqueue_request = ipmi_polled_enqueue_request; 214 sc->ipmi.ipmi_driver_requests_polled = 1; 215 sc->ipmi.ipmi_dev = dev; 216 217 sc->sc_msg = malloc(sizeof(struct opal_ipmi_msg) + IPMI_MAX_RX, M_IPMI, 218 M_WAITOK | M_ZERO); 219 220 return (ipmi_attach(dev)); 221 } 222 223 static int 224 opal_ipmi_detach(device_t dev) 225 { 226 return (EBUSY); 227 } 228 229 static device_method_t opal_ipmi_methods[] = { 230 /* Device interface */ 231 DEVMETHOD(device_probe, opal_ipmi_probe), 232 DEVMETHOD(device_attach, opal_ipmi_attach), 233 DEVMETHOD(device_detach, opal_ipmi_detach), 234 DEVMETHOD_END 235 }; 236 237 static driver_t opal_ipmi_driver = { 238 "ipmi", 239 opal_ipmi_methods, 240 sizeof(struct opal_ipmi_softc) 241 }; 242 243 DRIVER_MODULE(opal_ipmi, opal, opal_ipmi_driver, ipmi_devclass, NULL, NULL); 244