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 /* 53 * Request management. 54 */ 55 56 /* Allocate a new request with request and reply buffers. */ 57 struct ipmi_request * 58 ipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr, 59 uint8_t command, size_t requestlen, size_t replylen) 60 { 61 struct ipmi_request *req; 62 63 req = kmem_zalloc(sizeof (struct ipmi_request) + requestlen + replylen, 64 KM_SLEEP); 65 req->ir_sz = sizeof (struct ipmi_request) + requestlen + replylen; 66 req->ir_owner = dev; 67 req->ir_msgid = msgid; 68 req->ir_addr = addr; 69 req->ir_command = command; 70 if (requestlen) { 71 req->ir_request = (uchar_t *)&req[1]; 72 req->ir_requestlen = requestlen; 73 } 74 if (replylen) { 75 req->ir_reply = (uchar_t *)&req[1] + requestlen; 76 req->ir_replybuflen = replylen; 77 } 78 79 cv_init(&req->ir_cv, NULL, CV_DEFAULT, NULL); 80 req->ir_status = IRS_ALLOCATED; 81 82 return (req); 83 } 84 85 /* Free a request no longer in use. */ 86 void 87 ipmi_free_request(struct ipmi_request *req) 88 { 89 if (req == NULL) 90 return; 91 92 cv_destroy(&req->ir_cv); 93 94 kmem_free(req, req->ir_sz); 95 } 96 97 /* Store a processed request on the appropriate completion queue. */ 98 /*ARGSUSED*/ 99 void 100 ipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req) 101 { 102 struct ipmi_device *dev; 103 104 IPMI_LOCK_ASSERT(sc); 105 106 if (req->ir_status == IRS_CANCELED) { 107 ASSERT(req->ir_owner == NULL); 108 ipmi_free_request(req); 109 return; 110 } 111 112 req->ir_status = IRS_COMPLETED; 113 114 /* 115 * Anonymous requests (from inside the driver) always have a 116 * waiter that we awaken. 117 */ 118 if (req->ir_owner == NULL) { 119 cv_signal(&req->ir_cv); 120 } else { 121 dev = req->ir_owner; 122 TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link); 123 pollwakeup(&dev->ipmi_pollhead, POLLIN | POLLRDNORM); 124 125 dev->ipmi_status &= ~IPMI_BUSY; 126 if (dev->ipmi_status & IPMI_CLOSING) 127 cv_signal(&dev->ipmi_cv); 128 } 129 } 130 131 /* 132 * Enqueue an internal driver request and wait until it is completed. 133 */ 134 static int 135 ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request **preq, 136 int timo) 137 { 138 int error; 139 struct ipmi_request *req = *preq; 140 141 ASSERT(req->ir_owner == NULL); 142 143 IPMI_LOCK(sc); 144 error = sc->ipmi_enqueue_request(sc, req); 145 146 if (error != 0) { 147 IPMI_UNLOCK(sc); 148 return (error); 149 } 150 151 while (req->ir_status != IRS_COMPLETED && error >= 0) 152 if (timo == 0) 153 cv_wait(&req->ir_cv, &sc->ipmi_lock); 154 else 155 error = cv_timedwait(&req->ir_cv, &sc->ipmi_lock, 156 ddi_get_lbolt() + timo); 157 158 switch (req->ir_status) { 159 case IRS_QUEUED: 160 TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); 161 req->ir_status = IRS_CANCELED; 162 error = EWOULDBLOCK; 163 break; 164 case IRS_PROCESSED: 165 req->ir_status = IRS_CANCELED; 166 error = EWOULDBLOCK; 167 *preq = NULL; 168 break; 169 case IRS_COMPLETED: 170 error = req->ir_error; 171 break; 172 default: 173 panic("IPMI: Invalid request status"); 174 break; 175 } 176 IPMI_UNLOCK(sc); 177 178 return (error); 179 } 180 181 /* 182 * Helper routine for polled system interfaces that use 183 * ipmi_polled_enqueue_request() to queue requests. This request 184 * waits until there is a pending request and then returns the first 185 * request. If the driver is shutting down, it returns NULL. 186 */ 187 struct ipmi_request * 188 ipmi_dequeue_request(struct ipmi_softc *sc) 189 { 190 struct ipmi_request *req; 191 192 IPMI_LOCK_ASSERT(sc); 193 194 while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests)) 195 cv_wait(&sc->ipmi_request_added, &sc->ipmi_lock); 196 if (sc->ipmi_detaching) 197 return (NULL); 198 199 req = TAILQ_FIRST(&sc->ipmi_pending_requests); 200 TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); 201 req->ir_status = IRS_PROCESSED; 202 203 if (req->ir_owner != NULL) 204 req->ir_owner->ipmi_status |= IPMI_BUSY; 205 206 return (req); 207 } 208 209 int 210 ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req) 211 { 212 213 IPMI_LOCK_ASSERT(sc); 214 215 TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link); 216 req->ir_status = IRS_QUEUED; 217 cv_signal(&sc->ipmi_request_added); 218 return (0); 219 } 220 221 void 222 ipmi_shutdown(struct ipmi_softc *sc) 223 { 224 taskq_destroy(sc->ipmi_kthread); 225 226 cv_destroy(&sc->ipmi_request_added); 227 mutex_destroy(&sc->ipmi_lock); 228 } 229 230 boolean_t 231 ipmi_startup(struct ipmi_softc *sc) 232 { 233 struct ipmi_request *req; 234 int error, i; 235 236 /* Initialize interface-independent state. */ 237 mutex_init(&sc->ipmi_lock, NULL, MUTEX_DEFAULT, NULL); 238 cv_init(&sc->ipmi_request_added, NULL, CV_DEFAULT, NULL); 239 TAILQ_INIT(&sc->ipmi_pending_requests); 240 241 /* Initialize interface-dependent state. */ 242 error = sc->ipmi_startup(sc); 243 if (error) { 244 cmn_err(CE_WARN, "Failed to initialize interface: %d", error); 245 return (B_FALSE); 246 } 247 248 /* Send a GET_DEVICE_ID request. */ 249 req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 250 IPMI_GET_DEVICE_ID, 0, 15); 251 252 error = ipmi_submit_driver_request(sc, &req, MAX_TIMEOUT); 253 if (error == EWOULDBLOCK) { 254 cmn_err(CE_WARN, "Timed out waiting for GET_DEVICE_ID"); 255 ipmi_free_request(req); 256 return (B_FALSE); 257 } else if (error) { 258 cmn_err(CE_WARN, "Failed GET_DEVICE_ID: %d", error); 259 ipmi_free_request(req); 260 return (B_FALSE); 261 } else if (req->ir_compcode != 0) { 262 cmn_err(CE_WARN, 263 "Bad completion code for GET_DEVICE_ID: %d", 264 req->ir_compcode); 265 ipmi_free_request(req); 266 return (B_FALSE); 267 } else if (req->ir_replylen < 5) { 268 cmn_err(CE_WARN, "Short reply for GET_DEVICE_ID: %d", 269 req->ir_replylen); 270 ipmi_free_request(req); 271 return (B_FALSE); 272 } 273 274 cmn_err(CE_CONT, "!device rev. %d, firmware rev. %d.%d%d, " 275 "version %d.%d", 276 req->ir_reply[1] & 0x0f, req->ir_reply[2] & 0x7f, 277 req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f, 278 req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4); 279 280 ipmi_free_request(req); 281 282 req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 283 IPMI_CLEAR_FLAGS, 1, 0); 284 285 if ((error = ipmi_submit_driver_request(sc, &req, 0)) != 0) { 286 cmn_err(CE_WARN, "Failed to clear IPMI flags: %d\n", error); 287 ipmi_free_request(req); 288 return (B_FALSE); 289 } 290 291 /* Magic numbers */ 292 if (req->ir_compcode == 0xc0) { 293 cmn_err(CE_NOTE, "!Clear flags is busy"); 294 } 295 if (req->ir_compcode == 0xc1) { 296 cmn_err(CE_NOTE, "!Clear flags illegal"); 297 } 298 ipmi_free_request(req); 299 300 for (i = 0; i < 8; i++) { 301 req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 302 IPMI_GET_CHANNEL_INFO, 1, 0); 303 req->ir_request[0] = (uchar_t)i; 304 305 if (ipmi_submit_driver_request(sc, &req, 0) != 0) { 306 ipmi_free_request(req); 307 break; 308 } 309 310 if (req->ir_compcode != 0) { 311 ipmi_free_request(req); 312 break; 313 } 314 ipmi_free_request(req); 315 } 316 cmn_err(CE_CONT, "!number of channels %d", i); 317 318 /* probe for watchdog */ 319 req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 320 IPMI_GET_WDOG, 0, 0); 321 322 if ((error = ipmi_submit_driver_request(sc, &req, 0)) != 0) { 323 cmn_err(CE_WARN, "Failed to check IPMI watchdog: %d\n", error); 324 ipmi_free_request(req); 325 return (B_FALSE); 326 } 327 328 if (req->ir_compcode == 0x00) { 329 cmn_err(CE_CONT, "!watchdog supported"); 330 331 /* 332 * Here is where we could register a watchdog event handler. 333 * See ipmi_wd_event() in the FreeBSD code. 334 */ 335 } 336 ipmi_free_request(req); 337 338 return (B_TRUE); 339 } 340