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/rman.h> 38 #include <sys/selinfo.h> 39 #include <machine/bus.h> 40 41 #ifdef LOCAL_MODULE 42 #include <ipmi.h> 43 #include <ipmivars.h> 44 #else 45 #include <sys/ipmi.h> 46 #include <dev/ipmi/ipmivars.h> 47 #endif 48 49 static void smic_wait_for_tx_okay(struct ipmi_softc *); 50 static void smic_wait_for_rx_okay(struct ipmi_softc *); 51 static void smic_wait_for_not_busy(struct ipmi_softc *); 52 static void smic_set_busy(struct ipmi_softc *); 53 54 static void 55 smic_wait_for_tx_okay(struct ipmi_softc *sc) 56 { 57 int flags; 58 59 do { 60 flags = INB(sc, SMIC_FLAGS); 61 } while (!(flags & SMIC_STATUS_TX_RDY)); 62 } 63 64 static void 65 smic_wait_for_rx_okay(struct ipmi_softc *sc) 66 { 67 int flags; 68 69 do { 70 flags = INB(sc, SMIC_FLAGS); 71 } while (!(flags & SMIC_STATUS_RX_RDY)); 72 } 73 74 static void 75 smic_wait_for_not_busy(struct ipmi_softc *sc) 76 { 77 int flags; 78 79 do { 80 flags = INB(sc, SMIC_FLAGS); 81 } while (flags & SMIC_STATUS_BUSY); 82 } 83 84 static void 85 smic_set_busy(struct ipmi_softc *sc) 86 { 87 int flags; 88 89 flags = INB(sc, SMIC_FLAGS); 90 flags |= SMIC_STATUS_BUSY; 91 flags &= ~SMIC_STATUS_RESERVED; 92 OUTB(sc, SMIC_FLAGS, flags); 93 } 94 95 /* 96 * Start a transfer with a WR_START transaction that sends the NetFn/LUN 97 * address. 98 */ 99 static int 100 smic_start_write(struct ipmi_softc *sc, u_char data) 101 { 102 u_char error, status; 103 104 smic_wait_for_not_busy(sc); 105 106 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_START); 107 OUTB(sc, SMIC_DATA, data); 108 smic_set_busy(sc); 109 smic_wait_for_not_busy(sc); 110 status = INB(sc, SMIC_CTL_STS); 111 if (status != SMIC_SC_SMS_WR_START) { 112 error = INB(sc, SMIC_DATA); 113 device_printf(sc->ipmi_dev, "SMIC: Write did not start %02x\n", 114 error); 115 return (0); 116 } 117 return (1); 118 } 119 120 /* 121 * Write a byte in the middle of the message (either the command or one of 122 * the data bytes) using a WR_NEXT transaction. 123 */ 124 static int 125 smic_write_next(struct ipmi_softc *sc, u_char data) 126 { 127 u_char error, status; 128 129 smic_wait_for_tx_okay(sc); 130 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_NEXT); 131 OUTB(sc, SMIC_DATA, data); 132 smic_set_busy(sc); 133 smic_wait_for_not_busy(sc); 134 status = INB(sc, SMIC_CTL_STS); 135 if (status != SMIC_SC_SMS_WR_NEXT) { 136 error = INB(sc, SMIC_DATA); 137 device_printf(sc->ipmi_dev, "SMIC: Write did not next %02x\n", 138 error); 139 return (0); 140 } 141 return (1); 142 } 143 144 /* 145 * Write the last byte of a transfer to end the write phase via a WR_END 146 * transaction. 147 */ 148 static int 149 smic_write_last(struct ipmi_softc *sc, u_char data) 150 { 151 u_char error, status; 152 153 smic_wait_for_tx_okay(sc); 154 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_WR_END); 155 OUTB(sc, SMIC_DATA, data); 156 smic_set_busy(sc); 157 smic_wait_for_not_busy(sc); 158 status = INB(sc, SMIC_CTL_STS); 159 if (status != SMIC_SC_SMS_WR_END) { 160 error = INB(sc, SMIC_DATA); 161 device_printf(sc->ipmi_dev, "SMIC: Write did not end %02x\n", 162 error); 163 return (0); 164 } 165 return (1); 166 } 167 168 /* 169 * Start the read phase of a transfer with a RD_START transaction. 170 */ 171 static int 172 smic_start_read(struct ipmi_softc *sc, u_char *data) 173 { 174 u_char error, status; 175 176 smic_wait_for_not_busy(sc); 177 178 smic_wait_for_rx_okay(sc); 179 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_START); 180 smic_set_busy(sc); 181 smic_wait_for_not_busy(sc); 182 status = INB(sc, SMIC_CTL_STS); 183 if (status != SMIC_SC_SMS_RD_START) { 184 error = INB(sc, SMIC_DATA); 185 device_printf(sc->ipmi_dev, "SMIC: Read did not start %02x\n", 186 error); 187 return (0); 188 } 189 *data = INB(sc, SMIC_DATA); 190 return (1); 191 } 192 193 /* 194 * Read a byte via a RD_NEXT transaction. If this was the last byte, return 195 * 2 rather than 1. 196 */ 197 static int 198 smic_read_byte(struct ipmi_softc *sc, u_char *data) 199 { 200 u_char error, status; 201 202 smic_wait_for_rx_okay(sc); 203 OUTB(sc, SMIC_CTL_STS, SMIC_SC_SMS_RD_NEXT); 204 smic_set_busy(sc); 205 smic_wait_for_not_busy(sc); 206 status = INB(sc, SMIC_CTL_STS); 207 if (status != SMIC_SC_SMS_RD_NEXT && 208 status != SMIC_SC_SMS_RD_END) { 209 error = INB(sc, SMIC_DATA); 210 device_printf(sc->ipmi_dev, "SMIC: Read did not next %02x\n", 211 error); 212 return (0); 213 } 214 *data = INB(sc, SMIC_DATA); 215 if (status == SMIC_SC_SMS_RD_NEXT) 216 return (1); 217 else 218 return (2); 219 } 220 221 /* Complete a transfer via a RD_END transaction after reading the last byte. */ 222 static int 223 smic_read_end(struct ipmi_softc *sc) 224 { 225 u_char error, status; 226 227 OUTB(sc, SMIC_CTL_STS, SMIC_CC_SMS_RD_END); 228 smic_set_busy(sc); 229 smic_wait_for_not_busy(sc); 230 status = INB(sc, SMIC_CTL_STS); 231 if (status != SMIC_SC_SMS_RDY) { 232 error = INB(sc, SMIC_DATA); 233 device_printf(sc->ipmi_dev, "SMIC: Read did not end %02x\n", 234 error); 235 return (0); 236 } 237 return (1); 238 } 239 240 static int 241 smic_polled_request(struct ipmi_softc *sc, struct ipmi_request *req) 242 { 243 u_char *cp, data; 244 int i, state; 245 246 /* First, start the message with the address. */ 247 if (!smic_start_write(sc, req->ir_addr)) 248 return (0); 249 #ifdef SMIC_DEBUG 250 device_printf(sc->ipmi_dev, "SMIC: WRITE_START address: %02x\n", 251 req->ir_addr); 252 #endif 253 254 if (req->ir_requestlen == 0) { 255 /* Send the command as the last byte. */ 256 if (!smic_write_last(sc, req->ir_command)) 257 return (0); 258 #ifdef SMIC_DEBUG 259 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n", 260 req->ir_command); 261 #endif 262 } else { 263 /* Send the command. */ 264 if (!smic_write_next(sc, req->ir_command)) 265 return (0); 266 #ifdef SMIC_DEBUG 267 device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n", 268 req->ir_command); 269 #endif 270 271 /* Send the payload. */ 272 cp = req->ir_request; 273 for (i = 0; i < req->ir_requestlen - 1; i++) { 274 if (!smic_write_next(sc, *cp++)) 275 return (0); 276 #ifdef SMIC_DEBUG 277 device_printf(sc->ipmi_dev, "SMIC: Wrote data: %02x\n", 278 cp[-1]); 279 #endif 280 } 281 if (!smic_write_last(sc, *cp)) 282 return (0); 283 #ifdef SMIC_DEBUG 284 device_printf(sc->ipmi_dev, "SMIC: Write last data: %02x\n", 285 *cp); 286 #endif 287 } 288 289 /* Start the read phase by reading the NetFn/LUN. */ 290 if (smic_start_read(sc, &data) != 1) 291 return (0); 292 #ifdef SMIC_DEBUG 293 device_printf(sc->ipmi_dev, "SMIC: Read address: %02x\n", data); 294 #endif 295 if (data != IPMI_REPLY_ADDR(req->ir_addr)) { 296 device_printf(sc->ipmi_dev, "SMIC: Reply address mismatch\n"); 297 return (0); 298 } 299 300 /* Read the command. */ 301 if (smic_read_byte(sc, &data) != 1) 302 return (0); 303 #ifdef SMIC_DEBUG 304 device_printf(sc->ipmi_dev, "SMIC: Read command: %02x\n", data); 305 #endif 306 if (data != req->ir_command) { 307 device_printf(sc->ipmi_dev, "SMIC: Command mismatch\n"); 308 return (0); 309 } 310 311 /* Read the completion code. */ 312 state = smic_read_byte(sc, &req->ir_compcode); 313 if (state == 0) 314 return (0); 315 #ifdef SMIC_DEBUG 316 device_printf(sc->ipmi_dev, "SMIC: Read completion code: %02x\n", 317 req->ir_compcode); 318 #endif 319 320 /* Finally, read the reply from the BMC. */ 321 i = 0; 322 while (state == 1) { 323 state = smic_read_byte(sc, &data); 324 if (state == 0) 325 return (0); 326 if (i < req->ir_replybuflen) { 327 req->ir_reply[i] = data; 328 #ifdef SMIC_DEBUG 329 device_printf(sc->ipmi_dev, "SMIC: Read data: %02x\n", 330 data); 331 } else { 332 device_printf(sc->ipmi_dev, 333 "SMIC: Read short %02x byte %d\n", data, i + 1); 334 #endif 335 } 336 i++; 337 } 338 339 /* Terminate the transfer. */ 340 if (!smic_read_end(sc)) 341 return (0); 342 req->ir_replylen = i; 343 #ifdef SMIC_DEBUG 344 device_printf(sc->ipmi_dev, "SMIC: READ finished (%d bytes)\n", i); 345 if (req->ir_replybuflen < i) 346 #else 347 if (req->ir_replybuflen < i && req->ir_replybuflen != 0) 348 #endif 349 device_printf(sc->ipmi_dev, 350 "SMIC: Read short: %zd buffer, %d actual\n", 351 req->ir_replybuflen, i); 352 return (1); 353 } 354 355 static void 356 smic_loop(void *arg) 357 { 358 struct ipmi_softc *sc = arg; 359 struct ipmi_request *req; 360 int i, ok; 361 362 IPMI_LOCK(sc); 363 while ((req = ipmi_dequeue_request(sc)) != NULL) { 364 IPMI_UNLOCK(sc); 365 ok = 0; 366 for (i = 0; i < 3 && !ok; i++) { 367 IPMI_IO_LOCK(sc); 368 ok = smic_polled_request(sc, req); 369 IPMI_IO_UNLOCK(sc); 370 } 371 if (ok) 372 req->ir_error = 0; 373 else 374 req->ir_error = EIO; 375 IPMI_LOCK(sc); 376 ipmi_complete_request(sc, req); 377 } 378 IPMI_UNLOCK(sc); 379 kproc_exit(0); 380 } 381 382 static int 383 smic_startup(struct ipmi_softc *sc) 384 { 385 386 return (kproc_create(smic_loop, sc, &sc->ipmi_kthread, 0, 0, 387 "%s: smic", device_get_nameunit(sc->ipmi_dev))); 388 } 389 390 static int 391 smic_driver_request(struct ipmi_softc *sc, struct ipmi_request *req) 392 { 393 int i, ok; 394 395 ok = 0; 396 for (i = 0; i < 3 && !ok; i++) { 397 IPMI_IO_LOCK(sc); 398 ok = smic_polled_request(sc, req); 399 IPMI_IO_UNLOCK(sc); 400 } 401 if (ok) 402 req->ir_error = 0; 403 else 404 req->ir_error = EIO; 405 return (req->ir_error); 406 } 407 408 int 409 ipmi_smic_attach(struct ipmi_softc *sc) 410 { 411 int flags; 412 413 /* Setup function pointers. */ 414 sc->ipmi_startup = smic_startup; 415 sc->ipmi_enqueue_request = ipmi_polled_enqueue_request; 416 sc->ipmi_driver_request = smic_driver_request; 417 sc->ipmi_driver_requests_polled = 1; 418 419 /* See if we can talk to the controller. */ 420 flags = INB(sc, SMIC_FLAGS); 421 if (flags == 0xff) { 422 device_printf(sc->ipmi_dev, "couldn't find it\n"); 423 return (ENXIO); 424 } 425 426 #ifdef SMIC_DEBUG 427 device_printf(sc->ipmi_dev, "SMIC: initial state: %02x\n", flags); 428 #endif 429 430 return (0); 431 } 432