1 /* 2 * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 /* $FreeBSD: src/sys/dev/ipmi/ipmi.c,v 1.16 2011/11/07 15:43:11 ed Exp $ */ 28 29 /* 30 * Copyright 2012, Joyent, Inc. All rights reserved. 31 * Copyright 2013, Nexenta Systems, Inc. All rights reserved. 32 */ 33 34 #include <sys/devops.h> 35 #include <sys/conf.h> 36 #include <sys/modctl.h> 37 #include <sys/types.h> 38 #include <sys/file.h> 39 #include <sys/errno.h> 40 #include <sys/open.h> 41 #include <sys/cred.h> 42 #include <sys/uio.h> 43 #include <sys/stat.h> 44 #include <sys/cmn_err.h> 45 #include <sys/ddi.h> 46 #include <sys/sunddi.h> 47 #include <sys/smbios.h> 48 #include <sys/smbios_impl.h> 49 #include <sys/ipmi.h> 50 #include "ipmivars.h" 51 52 static kmutex_t slpmutex; 53 static kcondvar_t slplock; 54 55 /* 56 * Request management. 57 */ 58 59 /* Allocate a new request with request and reply buffers. */ 60 struct ipmi_request * 61 ipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr, 62 uint8_t command, size_t requestlen, size_t replylen) 63 { 64 struct ipmi_request *req; 65 66 req = kmem_zalloc(sizeof (struct ipmi_request) + requestlen + replylen, 67 KM_SLEEP); 68 req->ir_sz = sizeof (struct ipmi_request) + requestlen + replylen; 69 req->ir_owner = dev; 70 req->ir_msgid = msgid; 71 req->ir_addr = addr; 72 req->ir_command = command; 73 if (requestlen) { 74 req->ir_request = (uchar_t *)&req[1]; 75 req->ir_requestlen = requestlen; 76 } 77 if (replylen) { 78 req->ir_reply = (uchar_t *)&req[1] + requestlen; 79 req->ir_replybuflen = replylen; 80 } 81 return (req); 82 } 83 84 /* Free a request no longer in use. */ 85 void 86 ipmi_free_request(struct ipmi_request *req) 87 { 88 kmem_free(req, req->ir_sz); 89 } 90 91 /* Store a processed request on the appropriate completion queue. */ 92 /*ARGSUSED*/ 93 void 94 ipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req) 95 { 96 struct ipmi_device *dev; 97 98 IPMI_LOCK_ASSERT(sc); 99 100 /* 101 * Anonymous requests (from inside the driver) always have a 102 * waiter that we awaken. 103 */ 104 if (req->ir_owner == NULL) { 105 mutex_enter(&slpmutex); 106 cv_signal(&slplock); 107 mutex_exit(&slpmutex); 108 } else { 109 dev = req->ir_owner; 110 TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link); 111 pollwakeup(dev->ipmi_pollhead, POLLIN | POLLRDNORM); 112 } 113 } 114 115 /* 116 * Enqueue an internal driver request and wait until it is completed. 117 */ 118 static int 119 ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, 120 int timo) 121 { 122 int error; 123 124 IPMI_LOCK(sc); 125 error = sc->ipmi_enqueue_request(sc, req); 126 if (error == 0) { 127 /* Wait for result - see ipmi_complete_request */ 128 IPMI_UNLOCK(sc); 129 mutex_enter(&slpmutex); 130 if (timo == 0) 131 cv_wait(&slplock, &slpmutex); 132 else 133 error = cv_timedwait(&slplock, &slpmutex, 134 ddi_get_lbolt() + timo); 135 mutex_exit(&slpmutex); 136 IPMI_LOCK(sc); 137 if (error == -1) 138 error = EWOULDBLOCK; 139 else 140 error = req->ir_error; 141 } 142 IPMI_UNLOCK(sc); 143 144 return (error); 145 } 146 147 /* 148 * Helper routine for polled system interfaces that use 149 * ipmi_polled_enqueue_request() to queue requests. This request 150 * waits until there is a pending request and then returns the first 151 * request. If the driver is shutting down, it returns NULL. 152 */ 153 struct ipmi_request * 154 ipmi_dequeue_request(struct ipmi_softc *sc) 155 { 156 struct ipmi_request *req; 157 158 IPMI_LOCK_ASSERT(sc); 159 160 while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests)) 161 cv_wait(&sc->ipmi_request_added, &sc->ipmi_lock); 162 if (sc->ipmi_detaching) 163 return (NULL); 164 165 req = TAILQ_FIRST(&sc->ipmi_pending_requests); 166 TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); 167 return (req); 168 } 169 170 int 171 ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req) 172 { 173 174 IPMI_LOCK_ASSERT(sc); 175 176 TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link); 177 cv_signal(&sc->ipmi_request_added); 178 return (0); 179 } 180 181 void 182 ipmi_shutdown(struct ipmi_softc *sc) 183 { 184 taskq_destroy(sc->ipmi_kthread); 185 186 cv_destroy(&sc->ipmi_request_added); 187 mutex_destroy(&sc->ipmi_lock); 188 189 cv_destroy(&slplock); 190 mutex_destroy(&slpmutex); 191 } 192 193 boolean_t 194 ipmi_startup(struct ipmi_softc *sc) 195 { 196 struct ipmi_request *req; 197 int error, i; 198 199 mutex_init(&slpmutex, NULL, MUTEX_DEFAULT, NULL); 200 cv_init(&slplock, NULL, CV_DEFAULT, NULL); 201 202 /* Initialize interface-independent state. */ 203 mutex_init(&sc->ipmi_lock, NULL, MUTEX_DEFAULT, NULL); 204 cv_init(&sc->ipmi_request_added, NULL, CV_DEFAULT, NULL); 205 TAILQ_INIT(&sc->ipmi_pending_requests); 206 207 /* Initialize interface-dependent state. */ 208 error = sc->ipmi_startup(sc); 209 if (error) { 210 cmn_err(CE_WARN, "Failed to initialize interface: %d", error); 211 return (B_FALSE); 212 } 213 214 /* Send a GET_DEVICE_ID request. */ 215 req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 216 IPMI_GET_DEVICE_ID, 0, 15); 217 218 error = ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); 219 if (error == EWOULDBLOCK) { 220 cmn_err(CE_WARN, "Timed out waiting for GET_DEVICE_ID"); 221 ipmi_free_request(req); 222 return (B_FALSE); 223 } else if (error) { 224 cmn_err(CE_WARN, "Failed GET_DEVICE_ID: %d", error); 225 ipmi_free_request(req); 226 return (B_FALSE); 227 } else if (req->ir_compcode != 0) { 228 cmn_err(CE_WARN, 229 "Bad completion code for GET_DEVICE_ID: %d", 230 req->ir_compcode); 231 ipmi_free_request(req); 232 return (B_FALSE); 233 } else if (req->ir_replylen < 5) { 234 cmn_err(CE_WARN, "Short reply for GET_DEVICE_ID: %d", 235 req->ir_replylen); 236 ipmi_free_request(req); 237 return (B_FALSE); 238 } 239 240 cmn_err(CE_CONT, "!device rev. %d, firmware rev. %d.%d%d, " 241 "version %d.%d", 242 req->ir_reply[1] & 0x0f, req->ir_reply[2] & 0x7f, 243 req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f, 244 req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4); 245 246 ipmi_free_request(req); 247 248 req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 249 IPMI_CLEAR_FLAGS, 1, 0); 250 251 if ((error = ipmi_submit_driver_request(sc, req, 0)) != 0) { 252 cmn_err(CE_WARN, "Failed to clear IPMI flags: %d\n", error); 253 ipmi_free_request(req); 254 return (B_FALSE); 255 } 256 257 /* Magic numbers */ 258 if (req->ir_compcode == 0xc0) { 259 cmn_err(CE_NOTE, "!Clear flags is busy"); 260 } 261 if (req->ir_compcode == 0xc1) { 262 cmn_err(CE_NOTE, "!Clear flags illegal"); 263 } 264 ipmi_free_request(req); 265 266 for (i = 0; i < 8; i++) { 267 req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 268 IPMI_GET_CHANNEL_INFO, 1, 0); 269 req->ir_request[0] = (uchar_t)i; 270 271 if (ipmi_submit_driver_request(sc, req, 0) != 0) { 272 ipmi_free_request(req); 273 break; 274 } 275 276 if (req->ir_compcode != 0) { 277 ipmi_free_request(req); 278 break; 279 } 280 ipmi_free_request(req); 281 } 282 cmn_err(CE_CONT, "!number of channels %d", i); 283 284 /* probe for watchdog */ 285 req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 286 IPMI_GET_WDOG, 0, 0); 287 288 if ((error = ipmi_submit_driver_request(sc, req, 0)) != 0) { 289 cmn_err(CE_WARN, "Failed to check IPMI watchdog: %d\n", error); 290 ipmi_free_request(req); 291 return (B_FALSE); 292 } 293 294 if (req->ir_compcode == 0x00) { 295 cmn_err(CE_CONT, "!watchdog supported"); 296 297 /* 298 * Here is where we could register a watchdog event handler. 299 * See ipmi_wd_event() in the FreeBSD code. 300 */ 301 } 302 ipmi_free_request(req); 303 304 return (B_TRUE); 305 } 306