1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/bus.h>
32 #include <sys/condvar.h>
33 #include <sys/eventhandler.h>
34 #include <sys/kernel.h>
35 #include <sys/kthread.h>
36 #include <sys/module.h>
37 #include <sys/selinfo.h>
38
39 #include <dev/smbus/smbconf.h>
40 #include <dev/smbus/smb.h>
41
42 #include "smbus_if.h"
43
44 #ifdef LOCAL_MODULE
45 #include <ipmivars.h>
46 #else
47 #include <dev/ipmi/ipmivars.h>
48 #endif
49
50 #define SMBUS_WRITE_SINGLE 0x02
51 #define SMBUS_WRITE_START 0x06
52 #define SMBUS_WRITE_CONT 0x07
53 #define SMBUS_READ_START 0x03
54 #define SMBUS_READ_CONT 0x09
55 #define SMBUS_DATA_SIZE 32
56
57 #ifdef SSIF_DEBUG
58 static void
dump_buffer(device_t dev,const char * msg,u_char * bytes,int len)59 dump_buffer(device_t dev, const char *msg, u_char *bytes, int len)
60 {
61 int i;
62
63 device_printf(dev, "%s:", msg);
64 for (i = 0; i < len; i++)
65 printf(" %02x", bytes[i]);
66 printf("\n");
67 }
68 #endif
69
70 static int
ssif_polled_request(struct ipmi_softc * sc,struct ipmi_request * req)71 ssif_polled_request(struct ipmi_softc *sc, struct ipmi_request *req)
72 {
73 u_char ssif_buf[SMBUS_DATA_SIZE];
74 device_t dev = sc->ipmi_dev;
75 device_t smbus = sc->ipmi_ssif_smbus;
76 u_char *cp, block, count, offset;
77 size_t len;
78 int error;
79
80 /* Acquire the bus while we send the request. */
81 if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0)
82 return (0);
83
84 /*
85 * First, send out the request. Begin by filling out the first
86 * packet which includes the NetFn/LUN and command.
87 */
88 ssif_buf[0] = req->ir_addr;
89 ssif_buf[1] = req->ir_command;
90 if (req->ir_requestlen > 0)
91 bcopy(req->ir_request, &ssif_buf[2],
92 min(req->ir_requestlen, SMBUS_DATA_SIZE - 2));
93
94 /* Small requests are sent with a single command. */
95 if (req->ir_requestlen <= 30) {
96 #ifdef SSIF_DEBUG
97 dump_buffer(dev, "WRITE_SINGLE", ssif_buf,
98 req->ir_requestlen + 2);
99 #endif
100 error = smbus_error(smbus_bwrite(smbus,
101 sc->ipmi_ssif_smbus_address, SMBUS_WRITE_SINGLE,
102 req->ir_requestlen + 2, ssif_buf));
103 if (error) {
104 #ifdef SSIF_ERROR_DEBUG
105 device_printf(dev, "SSIF: WRITE_SINGLE error %d\n",
106 error);
107 #endif
108 goto fail;
109 }
110 } else {
111 /* Longer requests are sent out in 32-byte messages. */
112 #ifdef SSIF_DEBUG
113 dump_buffer(dev, "WRITE_START", ssif_buf, SMBUS_DATA_SIZE);
114 #endif
115 error = smbus_error(smbus_bwrite(smbus,
116 sc->ipmi_ssif_smbus_address, SMBUS_WRITE_START,
117 SMBUS_DATA_SIZE, ssif_buf));
118 if (error) {
119 #ifdef SSIF_ERROR_DEBUG
120 device_printf(dev, "SSIF: WRITE_START error %d\n",
121 error);
122 #endif
123 goto fail;
124 }
125
126 len = req->ir_requestlen - (SMBUS_DATA_SIZE - 2);
127 cp = req->ir_request + (SMBUS_DATA_SIZE - 2);
128 while (len > 0) {
129 #ifdef SSIF_DEBUG
130 dump_buffer(dev, "WRITE_CONT", cp,
131 min(len, SMBUS_DATA_SIZE));
132 #endif
133 error = smbus_error(smbus_bwrite(smbus,
134 sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT,
135 min(len, SMBUS_DATA_SIZE), cp));
136 if (error) {
137 #ifdef SSIF_ERROR_DEBUG
138 device_printf(dev, "SSIF: WRITE_CONT error %d\n",
139 error);
140 #endif
141 goto fail;
142 }
143 cp += SMBUS_DATA_SIZE;
144 len -= SMBUS_DATA_SIZE;
145 }
146
147 /*
148 * The final WRITE_CONT transaction has to have a non-zero
149 * length that is also not SMBUS_DATA_SIZE. If our last
150 * WRITE_CONT transaction in the loop sent SMBUS_DATA_SIZE
151 * bytes, then len will be 0, and we send an extra 0x00 byte
152 * to terminate the transaction.
153 */
154 if (len == 0) {
155 char c = 0;
156
157 #ifdef SSIF_DEBUG
158 dump_buffer(dev, "WRITE_CONT", &c, 1);
159 #endif
160 error = smbus_error(smbus_bwrite(smbus,
161 sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT,
162 1, &c));
163 if (error) {
164 #ifdef SSIF_ERROR_DEBUG
165 device_printf(dev, "SSIF: WRITE_CONT error %d\n",
166 error);
167 #endif
168 goto fail;
169 }
170 }
171 }
172
173 /* Release the bus. */
174 smbus_release_bus(smbus, dev);
175
176 /* Give the BMC 100ms to chew on the request. */
177 pause("ssifwt", hz / 10);
178
179 /* Try to read the first packet. */
180 read_start:
181 if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0)
182 return (0);
183 count = SMBUS_DATA_SIZE;
184 error = smbus_error(smbus_bread(smbus,
185 sc->ipmi_ssif_smbus_address, SMBUS_READ_START, &count, ssif_buf));
186 if (error == ENXIO || error == EBUSY) {
187 smbus_release_bus(smbus, dev);
188 #ifdef SSIF_DEBUG
189 device_printf(dev, "SSIF: READ_START retry\n");
190 #endif
191 /* Give the BMC another 10ms. */
192 pause("ssifwt", hz / 100);
193 goto read_start;
194 }
195 if (error) {
196 #ifdef SSIF_ERROR_DEBUG
197 device_printf(dev, "SSIF: READ_START failed: %d\n", error);
198 #endif
199 goto fail;
200 }
201 #ifdef SSIF_DEBUG
202 device_printf(dev, "SSIF: READ_START: ok\n");
203 #endif
204
205 /*
206 * If this is the first part of a multi-part read, then we need to
207 * skip the first two bytes.
208 */
209 if (count == SMBUS_DATA_SIZE && ssif_buf[0] == 0 && ssif_buf[1] == 1)
210 offset = 2;
211 else
212 offset = 0;
213
214 /* We had better get the reply header. */
215 if (count < 3) {
216 device_printf(dev, "SSIF: Short reply packet\n");
217 goto fail;
218 }
219
220 /* Verify the NetFn/LUN. */
221 if (ssif_buf[offset] != IPMI_REPLY_ADDR(req->ir_addr)) {
222 device_printf(dev, "SSIF: Reply address mismatch\n");
223 goto fail;
224 }
225
226 /* Verify the command. */
227 if (ssif_buf[offset + 1] != req->ir_command) {
228 device_printf(dev, "SMIC: Command mismatch\n");
229 goto fail;
230 }
231
232 /* Read the completion code. */
233 req->ir_compcode = ssif_buf[offset + 2];
234
235 /* If this is a single read, just copy the data and return. */
236 if (offset == 0) {
237 #ifdef SSIF_DEBUG
238 dump_buffer(dev, "READ_SINGLE", ssif_buf, count);
239 #endif
240 len = count - 3;
241 bcopy(&ssif_buf[3], req->ir_reply,
242 min(req->ir_replybuflen, len));
243 goto done;
244 }
245
246 /*
247 * This is the first part of a multi-read transaction, so copy
248 * out the payload and start looping.
249 */
250 #ifdef SSIF_DEBUG
251 dump_buffer(dev, "READ_START", ssif_buf + 2, count - 2);
252 #endif
253 bcopy(&ssif_buf[5], req->ir_reply, min(req->ir_replybuflen, count - 5));
254 len = count - 5;
255 block = 1;
256
257 for (;;) {
258 /* Read another packet via READ_CONT. */
259 count = SMBUS_DATA_SIZE;
260 error = smbus_error(smbus_bread(smbus,
261 sc->ipmi_ssif_smbus_address, SMBUS_READ_CONT, &count,
262 ssif_buf));
263 if (error) {
264 #ifdef SSIF_ERROR_DEBUG
265 printf("SSIF: READ_CONT failed: %d\n", error);
266 #endif
267 goto fail;
268 }
269 #ifdef SSIF_DEBUG
270 device_printf(dev, "SSIF: READ_CONT... ok\n");
271 #endif
272
273 /* Verify the block number. 0xff marks the last block. */
274 if (ssif_buf[0] != 0xff && ssif_buf[0] != block) {
275 device_printf(dev, "SSIF: Read wrong block %d %d\n",
276 ssif_buf[0], block);
277 goto fail;
278 }
279 if (ssif_buf[0] != 0xff && count < SMBUS_DATA_SIZE) {
280 device_printf(dev,
281 "SSIF: Read short middle block, length %d\n",
282 count);
283 goto fail;
284 }
285 #ifdef SSIF_DEBUG
286 if (ssif_buf[0] == 0xff)
287 dump_buffer(dev, "READ_END", ssif_buf + 1, count - 1);
288 else
289 dump_buffer(dev, "READ_CONT", ssif_buf + 1, count - 1);
290 #endif
291 if (len < req->ir_replybuflen)
292 bcopy(&ssif_buf[1], &req->ir_reply[len],
293 min(req->ir_replybuflen - len, count - 1));
294 len += count - 1;
295
296 /* If this was the last block we are done. */
297 if (ssif_buf[0] == 0xff)
298 break;
299 block++;
300 }
301
302 done:
303 /* Save the total length and return success. */
304 req->ir_replylen = len;
305 smbus_release_bus(smbus, dev);
306 return (1);
307
308 fail:
309 smbus_release_bus(smbus, dev);
310 return (0);
311 }
312
313 static void
ssif_loop(void * arg)314 ssif_loop(void *arg)
315 {
316 struct ipmi_softc *sc = arg;
317 struct ipmi_request *req;
318 int i, ok;
319
320 IPMI_LOCK(sc);
321 while ((req = ipmi_dequeue_request(sc)) != NULL) {
322 IPMI_UNLOCK(sc);
323 ok = 0;
324 for (i = 0; i < 5; i++) {
325 ok = ssif_polled_request(sc, req);
326 if (ok)
327 break;
328
329 /* Wait 60 ms between retries. */
330 pause("retry", 60 * hz / 1000);
331 #ifdef SSIF_RETRY_DEBUG
332 device_printf(sc->ipmi_dev,
333 "SSIF: Retrying request (%d)\n", i + 1);
334 #endif
335 }
336 if (ok)
337 req->ir_error = 0;
338 else
339 req->ir_error = EIO;
340 IPMI_LOCK(sc);
341 ipmi_complete_request(sc, req);
342 IPMI_UNLOCK(sc);
343
344 /* Enforce 10ms between requests. */
345 pause("delay", hz / 100);
346
347 IPMI_LOCK(sc);
348 }
349 IPMI_UNLOCK(sc);
350 kproc_exit(0);
351 }
352
353 static int
ssif_startup(struct ipmi_softc * sc)354 ssif_startup(struct ipmi_softc *sc)
355 {
356
357 return (kproc_create(ssif_loop, sc, &sc->ipmi_kthread, 0, 0,
358 "%s: ssif", device_get_nameunit(sc->ipmi_dev)));
359 }
360
361 static int
ssif_driver_request(struct ipmi_softc * sc,struct ipmi_request * req)362 ssif_driver_request(struct ipmi_softc *sc, struct ipmi_request *req)
363 {
364 int error;
365
366 IPMI_LOCK(sc);
367 error = ipmi_polled_enqueue_request(sc, req);
368 if (error == 0)
369 error = msleep(req, &sc->ipmi_requests_lock, 0, "ipmireq", 0);
370 if (error == 0)
371 error = req->ir_error;
372 IPMI_UNLOCK(sc);
373 return (error);
374 }
375
376 int
ipmi_ssif_attach(struct ipmi_softc * sc,device_t smbus,int smbus_address)377 ipmi_ssif_attach(struct ipmi_softc *sc, device_t smbus, int smbus_address)
378 {
379
380 /* Setup smbus address. */
381 sc->ipmi_ssif_smbus = smbus;
382 sc->ipmi_ssif_smbus_address = smbus_address;
383
384 /* Setup function pointers. */
385 sc->ipmi_startup = ssif_startup;
386 sc->ipmi_enqueue_request = ipmi_polled_enqueue_request;
387 sc->ipmi_driver_request = ssif_driver_request;
388
389 return (0);
390 }
391