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 */ 32 33 #include <sys/devops.h> 34 #include <sys/conf.h> 35 #include <sys/modctl.h> 36 #include <sys/types.h> 37 #include <sys/file.h> 38 #include <sys/errno.h> 39 #include <sys/open.h> 40 #include <sys/cred.h> 41 #include <sys/uio.h> 42 #include <sys/stat.h> 43 #include <sys/cmn_err.h> 44 #include <sys/ddi.h> 45 #include <sys/sunddi.h> 46 #include <sys/smbios.h> 47 #include <sys/smbios_impl.h> 48 #include <sys/ipmi.h> 49 #include "ipmivars.h" 50 51 static kmutex_t slpmutex; 52 static kcondvar_t slplock; 53 54 /* 55 * Request management. 56 */ 57 58 /* Allocate a new request with request and reply buffers. */ 59 struct ipmi_request * 60 ipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr, 61 uint8_t command, size_t requestlen, size_t replylen) 62 { 63 struct ipmi_request *req; 64 65 req = kmem_zalloc(sizeof (struct ipmi_request) + requestlen + replylen, 66 KM_SLEEP); 67 req->ir_sz = sizeof (struct ipmi_request) + requestlen + replylen; 68 req->ir_owner = dev; 69 req->ir_msgid = msgid; 70 req->ir_addr = addr; 71 req->ir_command = command; 72 if (requestlen) { 73 req->ir_request = (uchar_t *)&req[1]; 74 req->ir_requestlen = requestlen; 75 } 76 if (replylen) { 77 req->ir_reply = (uchar_t *)&req[1] + requestlen; 78 req->ir_replybuflen = replylen; 79 } 80 return (req); 81 } 82 83 /* Free a request no longer in use. */ 84 void 85 ipmi_free_request(struct ipmi_request *req) 86 { 87 kmem_free(req, req->ir_sz); 88 } 89 90 /* Store a processed request on the appropriate completion queue. */ 91 /*ARGSUSED*/ 92 void 93 ipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req) 94 { 95 struct ipmi_device *dev; 96 97 IPMI_LOCK_ASSERT(sc); 98 99 /* 100 * Anonymous requests (from inside the driver) always have a 101 * waiter that we awaken. 102 */ 103 if (req->ir_owner == NULL) { 104 mutex_enter(&slpmutex); 105 cv_signal(&slplock); 106 mutex_exit(&slpmutex); 107 } else { 108 dev = req->ir_owner; 109 TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link); 110 pollwakeup(dev->ipmi_pollhead, POLLIN | POLLRDNORM); 111 } 112 } 113 114 /* 115 * Enqueue an internal driver request and wait until it is completed. 116 */ 117 static int 118 ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, 119 int timo) 120 { 121 int error; 122 123 IPMI_LOCK(sc); 124 error = sc->ipmi_enqueue_request(sc, req); 125 if (error == 0) { 126 /* Wait for result - see ipmi_complete_request */ 127 IPMI_UNLOCK(sc); 128 mutex_enter(&slpmutex); 129 if (timo == 0) 130 cv_wait(&slplock, &slpmutex); 131 else 132 error = cv_timedwait(&slplock, &slpmutex, 133 ddi_get_lbolt() + timo); 134 mutex_exit(&slpmutex); 135 IPMI_LOCK(sc); 136 if (error == -1) 137 error = EWOULDBLOCK; 138 else 139 error = req->ir_error; 140 } 141 IPMI_UNLOCK(sc); 142 143 return (error); 144 } 145 146 /* 147 * Helper routine for polled system interfaces that use 148 * ipmi_polled_enqueue_request() to queue requests. This request 149 * waits until there is a pending request and then returns the first 150 * request. If the driver is shutting down, it returns NULL. 151 */ 152 struct ipmi_request * 153 ipmi_dequeue_request(struct ipmi_softc *sc) 154 { 155 struct ipmi_request *req; 156 157 IPMI_LOCK_ASSERT(sc); 158 159 while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests)) 160 cv_wait(&sc->ipmi_request_added, &sc->ipmi_lock); 161 if (sc->ipmi_detaching) 162 return (NULL); 163 164 req = TAILQ_FIRST(&sc->ipmi_pending_requests); 165 TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); 166 return (req); 167 } 168 169 int 170 ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req) 171 { 172 173 IPMI_LOCK_ASSERT(sc); 174 175 TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link); 176 cv_signal(&sc->ipmi_request_added); 177 return (0); 178 } 179 180 void 181 ipmi_startup(struct ipmi_softc *sc) 182 { 183 struct ipmi_request *req; 184 int error, i; 185 186 mutex_init(&slpmutex, NULL, MUTEX_DEFAULT, NULL); 187 cv_init(&slplock, NULL, CV_DEFAULT, NULL); 188 189 /* Initialize interface-independent state. */ 190 mutex_init(&sc->ipmi_lock, NULL, MUTEX_DEFAULT, NULL); 191 cv_init(&sc->ipmi_request_added, NULL, CV_DEFAULT, NULL); 192 TAILQ_INIT(&sc->ipmi_pending_requests); 193 194 /* Initialize interface-dependent state. */ 195 error = sc->ipmi_startup(sc); 196 if (error) { 197 cmn_err(CE_WARN, "Failed to initialize interface: %d", error); 198 return; 199 } 200 201 /* Send a GET_DEVICE_ID request. */ 202 req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 203 IPMI_GET_DEVICE_ID, 0, 15); 204 205 error = ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); 206 if (error == EWOULDBLOCK) { 207 cmn_err(CE_WARN, "Timed out waiting for GET_DEVICE_ID"); 208 ipmi_free_request(req); 209 return; 210 } else if (error) { 211 cmn_err(CE_WARN, "Failed GET_DEVICE_ID: %d", error); 212 ipmi_free_request(req); 213 return; 214 } else if (req->ir_compcode != 0) { 215 cmn_err(CE_WARN, 216 "Bad completion code for GET_DEVICE_ID: %d", 217 req->ir_compcode); 218 ipmi_free_request(req); 219 return; 220 } else if (req->ir_replylen < 5) { 221 cmn_err(CE_WARN, "Short reply for GET_DEVICE_ID: %d", 222 req->ir_replylen); 223 ipmi_free_request(req); 224 return; 225 } 226 227 cmn_err(CE_CONT, "!device rev. %d, firmware rev. %d.%d%d, " 228 "version %d.%d", 229 req->ir_reply[1] & 0x0f, req->ir_reply[2] & 0x7f, 230 req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f, 231 req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4); 232 233 ipmi_free_request(req); 234 235 req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 236 IPMI_CLEAR_FLAGS, 1, 0); 237 238 if ((error = ipmi_submit_driver_request(sc, req, 0)) != 0) 239 cmn_err(CE_WARN, "Failed to clear IPMI flags: %d\n", error); 240 241 /* Magic numbers */ 242 if (req->ir_compcode == 0xc0) { 243 cmn_err(CE_NOTE, "!Clear flags is busy"); 244 } 245 if (req->ir_compcode == 0xc1) { 246 cmn_err(CE_NOTE, "!Clear flags illegal"); 247 } 248 ipmi_free_request(req); 249 250 for (i = 0; i < 8; i++) { 251 req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 252 IPMI_GET_CHANNEL_INFO, 1, 0); 253 req->ir_request[0] = (uchar_t)i; 254 255 if (ipmi_submit_driver_request(sc, req, 0) != 0) { 256 ipmi_free_request(req); 257 break; 258 } 259 260 if (req->ir_compcode != 0) { 261 ipmi_free_request(req); 262 break; 263 } 264 ipmi_free_request(req); 265 } 266 cmn_err(CE_CONT, "!number of channels %d", i); 267 268 /* probe for watchdog */ 269 req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0), 270 IPMI_GET_WDOG, 0, 0); 271 272 if ((error = ipmi_submit_driver_request(sc, req, 0)) != 0) { 273 cmn_err(CE_WARN, "Failed to check IPMI watchdog: %d\n", error); 274 ipmi_free_request(req); 275 return; 276 } 277 278 if (req->ir_compcode == 0x00) { 279 cmn_err(CE_CONT, "!watchdog supported"); 280 281 /* 282 * Here is where we could register a watchdog event handler. 283 * See ipmi_wd_event() in the FreeBSD code. 284 */ 285 } 286 ipmi_free_request(req); 287 } 288