8354 sync regcomp(3C) with upstream (fix make catalog)
[unleashed/tickless.git] / usr / src / uts / intel / io / ipmi / ipmi.c
blob7fb5b757f61bea8193fc5702068a0f41eb11eb64
1 /*
2 * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
3 * All rights reserved.
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.
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.
27 /* $FreeBSD: src/sys/dev/ipmi/ipmi.c,v 1.16 2011/11/07 15:43:11 ed Exp $ */
30 * Copyright 2012, Joyent, Inc. All rights reserved.
31 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
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"
53 * Request management.
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)
61 struct ipmi_request *req;
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;
74 if (replylen) {
75 req->ir_reply = (uchar_t *)&req[1] + requestlen;
76 req->ir_replybuflen = replylen;
79 cv_init(&req->ir_cv, NULL, CV_DEFAULT, NULL);
80 req->ir_status = IRS_ALLOCATED;
82 return (req);
85 /* Free a request no longer in use. */
86 void
87 ipmi_free_request(struct ipmi_request *req)
89 if (req == NULL)
90 return;
92 cv_destroy(&req->ir_cv);
94 kmem_free(req, req->ir_sz);
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)
102 struct ipmi_device *dev;
104 IPMI_LOCK_ASSERT(sc);
106 if (req->ir_status == IRS_CANCELED) {
107 ASSERT(req->ir_owner == NULL);
108 ipmi_free_request(req);
109 return;
112 req->ir_status = IRS_COMPLETED;
115 * Anonymous requests (from inside the driver) always have a
116 * waiter that we awaken.
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);
125 dev->ipmi_status &= ~IPMI_BUSY;
126 if (dev->ipmi_status & IPMI_CLOSING)
127 cv_signal(&dev->ipmi_cv);
132 * Enqueue an internal driver request and wait until it is completed.
134 static int
135 ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request **preq,
136 int timo)
138 int error;
139 struct ipmi_request *req = *preq;
141 ASSERT(req->ir_owner == NULL);
143 IPMI_LOCK(sc);
144 error = sc->ipmi_enqueue_request(sc, req);
146 if (error != 0) {
147 IPMI_UNLOCK(sc);
148 return (error);
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);
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;
176 IPMI_UNLOCK(sc);
178 return (error);
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.
187 struct ipmi_request *
188 ipmi_dequeue_request(struct ipmi_softc *sc)
190 struct ipmi_request *req;
192 IPMI_LOCK_ASSERT(sc);
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);
199 req = TAILQ_FIRST(&sc->ipmi_pending_requests);
200 TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link);
201 req->ir_status = IRS_PROCESSED;
203 if (req->ir_owner != NULL)
204 req->ir_owner->ipmi_status |= IPMI_BUSY;
206 return (req);
210 ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req)
213 IPMI_LOCK_ASSERT(sc);
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);
221 void
222 ipmi_shutdown(struct ipmi_softc *sc)
224 taskq_destroy(sc->ipmi_kthread);
226 cv_destroy(&sc->ipmi_request_added);
227 mutex_destroy(&sc->ipmi_lock);
230 boolean_t
231 ipmi_startup(struct ipmi_softc *sc)
233 struct ipmi_request *req;
234 int error, i;
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);
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);
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);
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);
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);
280 ipmi_free_request(req);
282 req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
283 IPMI_CLEAR_FLAGS, 1, 0);
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);
291 /* Magic numbers */
292 if (req->ir_compcode == 0xc0) {
293 cmn_err(CE_NOTE, "!Clear flags is busy");
295 if (req->ir_compcode == 0xc1) {
296 cmn_err(CE_NOTE, "!Clear flags illegal");
298 ipmi_free_request(req);
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;
305 if (ipmi_submit_driver_request(sc, &req, 0) != 0) {
306 ipmi_free_request(req);
307 break;
310 if (req->ir_compcode != 0) {
311 ipmi_free_request(req);
312 break;
314 ipmi_free_request(req);
316 cmn_err(CE_CONT, "!number of channels %d", i);
318 /* probe for watchdog */
319 req = ipmi_alloc_driver_request(IPMI_ADDR(IPMI_APP_REQUEST, 0),
320 IPMI_GET_WDOG, 0, 0);
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);
328 if (req->ir_compcode == 0x00) {
329 cmn_err(CE_CONT, "!watchdog supported");
332 * Here is where we could register a watchdog event handler.
333 * See ipmi_wd_event() in the FreeBSD code.
336 ipmi_free_request(req);
338 return (B_TRUE);