137b1ce13SDoug Ambrisko /*- 237b1ce13SDoug Ambrisko * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com> 337b1ce13SDoug Ambrisko * All rights reserved. 437b1ce13SDoug Ambrisko * 537b1ce13SDoug Ambrisko * Redistribution and use in source and binary forms, with or without 637b1ce13SDoug Ambrisko * modification, are permitted provided that the following conditions 737b1ce13SDoug Ambrisko * are met: 837b1ce13SDoug Ambrisko * 1. Redistributions of source code must retain the above copyright 937b1ce13SDoug Ambrisko * notice, this list of conditions and the following disclaimer. 1037b1ce13SDoug Ambrisko * 2. Redistributions in binary form must reproduce the above copyright 1137b1ce13SDoug Ambrisko * notice, this list of conditions and the following disclaimer in the 1237b1ce13SDoug Ambrisko * documentation and/or other materials provided with the distribution. 1337b1ce13SDoug Ambrisko * 1437b1ce13SDoug Ambrisko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1537b1ce13SDoug Ambrisko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1637b1ce13SDoug Ambrisko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1737b1ce13SDoug Ambrisko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1837b1ce13SDoug Ambrisko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1937b1ce13SDoug Ambrisko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2037b1ce13SDoug Ambrisko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2137b1ce13SDoug Ambrisko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2237b1ce13SDoug Ambrisko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2337b1ce13SDoug Ambrisko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2437b1ce13SDoug Ambrisko * SUCH DAMAGE. 2537b1ce13SDoug Ambrisko */ 2637b1ce13SDoug Ambrisko 2737b1ce13SDoug Ambrisko #include <sys/cdefs.h> 2837b1ce13SDoug Ambrisko __FBSDID("$FreeBSD$"); 2937b1ce13SDoug Ambrisko 3037b1ce13SDoug Ambrisko #include <sys/param.h> 3137b1ce13SDoug Ambrisko #include <sys/systm.h> 32d72a0786SJohn Baldwin #include <sys/bus.h> 33d72a0786SJohn Baldwin #include <sys/condvar.h> 34d72a0786SJohn Baldwin #include <sys/conf.h> 3537b1ce13SDoug Ambrisko #include <sys/kernel.h> 3637b1ce13SDoug Ambrisko #include <sys/malloc.h> 3737b1ce13SDoug Ambrisko #include <sys/module.h> 38d72a0786SJohn Baldwin #include <sys/poll.h> 3937b1ce13SDoug Ambrisko #include <sys/rman.h> 40d72a0786SJohn Baldwin #include <sys/selinfo.h> 4137b1ce13SDoug Ambrisko #include <sys/sysctl.h> 42d72a0786SJohn Baldwin #include <sys/watchdog.h> 4337b1ce13SDoug Ambrisko 4437b1ce13SDoug Ambrisko #ifdef LOCAL_MODULE 4537b1ce13SDoug Ambrisko #include <ipmi.h> 4637b1ce13SDoug Ambrisko #include <ipmivars.h> 4737b1ce13SDoug Ambrisko #else 4837b1ce13SDoug Ambrisko #include <sys/ipmi.h> 4937b1ce13SDoug Ambrisko #include <dev/ipmi/ipmivars.h> 5037b1ce13SDoug Ambrisko #endif 5137b1ce13SDoug Ambrisko 52c869aa71SJohn Baldwin /* 53c869aa71SJohn Baldwin * Driver request structures are allocated on the stack via alloca() to 54c869aa71SJohn Baldwin * avoid calling malloc(), especially for the watchdog handler. 55c869aa71SJohn Baldwin * To avoid too much stack growth, a previously allocated structure can 56c869aa71SJohn Baldwin * be reused via IPMI_INIT_DRIVER_REQUEST(), but the caller should ensure 57c869aa71SJohn Baldwin * that there is adequate reply/request space in the original allocation. 58c869aa71SJohn Baldwin */ 59c869aa71SJohn Baldwin #define IPMI_INIT_DRIVER_REQUEST(req, addr, cmd, reqlen, replylen) \ 60c869aa71SJohn Baldwin bzero((req), sizeof(struct ipmi_request)); \ 61c869aa71SJohn Baldwin ipmi_init_request((req), NULL, 0, (addr), (cmd), (reqlen), (replylen)) 62c869aa71SJohn Baldwin 63c869aa71SJohn Baldwin #define IPMI_ALLOC_DRIVER_REQUEST(req, addr, cmd, reqlen, replylen) \ 64c869aa71SJohn Baldwin (req) = __builtin_alloca(sizeof(struct ipmi_request) + \ 65c869aa71SJohn Baldwin (reqlen) + (replylen)); \ 66c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST((req), (addr), (cmd), (reqlen), \ 67c869aa71SJohn Baldwin (replylen)) 68c869aa71SJohn Baldwin 6937b1ce13SDoug Ambrisko #ifdef IPMB 7037b1ce13SDoug Ambrisko static int ipmi_ipmb_checksum(u_char, int); 7137b1ce13SDoug Ambrisko static int ipmi_ipmb_send_message(device_t, u_char, u_char, u_char, 7237b1ce13SDoug Ambrisko u_char, u_char, int) 7337b1ce13SDoug Ambrisko #endif 7437b1ce13SDoug Ambrisko 7537b1ce13SDoug Ambrisko static d_ioctl_t ipmi_ioctl; 7637b1ce13SDoug Ambrisko static d_poll_t ipmi_poll; 7737b1ce13SDoug Ambrisko static d_open_t ipmi_open; 78943bebd2SJohn Baldwin static void ipmi_dtor(void *arg); 7937b1ce13SDoug Ambrisko 8037b1ce13SDoug Ambrisko int ipmi_attached = 0; 8137b1ce13SDoug Ambrisko 8237b1ce13SDoug Ambrisko static int on = 1; 836472ac3dSEd Schouten static SYSCTL_NODE(_hw, OID_AUTO, ipmi, CTLFLAG_RD, 0, 846472ac3dSEd Schouten "IPMI driver parameters"); 8537b1ce13SDoug Ambrisko SYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RW, 8637b1ce13SDoug Ambrisko &on, 0, ""); 8737b1ce13SDoug Ambrisko 8837b1ce13SDoug Ambrisko static struct cdevsw ipmi_cdevsw = { 8937b1ce13SDoug Ambrisko .d_version = D_VERSION, 9037b1ce13SDoug Ambrisko .d_open = ipmi_open, 9137b1ce13SDoug Ambrisko .d_ioctl = ipmi_ioctl, 9237b1ce13SDoug Ambrisko .d_poll = ipmi_poll, 9337b1ce13SDoug Ambrisko .d_name = "ipmi", 9437b1ce13SDoug Ambrisko }; 9537b1ce13SDoug Ambrisko 96d745c852SEd Schouten static MALLOC_DEFINE(M_IPMI, "ipmi", "ipmi"); 9737b1ce13SDoug Ambrisko 9837b1ce13SDoug Ambrisko static int 99d72a0786SJohn Baldwin ipmi_open(struct cdev *cdev, int flags, int fmt, struct thread *td) 10037b1ce13SDoug Ambrisko { 101d72a0786SJohn Baldwin struct ipmi_device *dev; 10237b1ce13SDoug Ambrisko struct ipmi_softc *sc; 103943bebd2SJohn Baldwin int error; 10437b1ce13SDoug Ambrisko 10537b1ce13SDoug Ambrisko if (!on) 106d72a0786SJohn Baldwin return (ENOENT); 10737b1ce13SDoug Ambrisko 108943bebd2SJohn Baldwin /* Initialize the per file descriptor data. */ 109943bebd2SJohn Baldwin dev = malloc(sizeof(struct ipmi_device), M_IPMI, M_WAITOK | M_ZERO); 110943bebd2SJohn Baldwin error = devfs_set_cdevpriv(dev, ipmi_dtor); 111943bebd2SJohn Baldwin if (error) { 112943bebd2SJohn Baldwin free(dev, M_IPMI); 113943bebd2SJohn Baldwin return (error); 11437b1ce13SDoug Ambrisko } 115943bebd2SJohn Baldwin 116943bebd2SJohn Baldwin sc = cdev->si_drv1; 117943bebd2SJohn Baldwin TAILQ_INIT(&dev->ipmi_completed_requests); 118943bebd2SJohn Baldwin dev->ipmi_address = IPMI_BMC_SLAVE_ADDR; 119943bebd2SJohn Baldwin dev->ipmi_lun = IPMI_BMC_SMS_LUN; 120943bebd2SJohn Baldwin dev->ipmi_softc = sc; 121943bebd2SJohn Baldwin IPMI_LOCK(sc); 122943bebd2SJohn Baldwin sc->ipmi_opened++; 123d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 12437b1ce13SDoug Ambrisko 125d72a0786SJohn Baldwin return (0); 12637b1ce13SDoug Ambrisko } 12737b1ce13SDoug Ambrisko 12837b1ce13SDoug Ambrisko static int 129d72a0786SJohn Baldwin ipmi_poll(struct cdev *cdev, int poll_events, struct thread *td) 13037b1ce13SDoug Ambrisko { 131d72a0786SJohn Baldwin struct ipmi_device *dev; 13237b1ce13SDoug Ambrisko struct ipmi_softc *sc; 13337b1ce13SDoug Ambrisko int revents = 0; 13437b1ce13SDoug Ambrisko 135943bebd2SJohn Baldwin if (devfs_get_cdevpriv((void **)&dev)) 136943bebd2SJohn Baldwin return (0); 13737b1ce13SDoug Ambrisko 138943bebd2SJohn Baldwin sc = cdev->si_drv1; 139d72a0786SJohn Baldwin IPMI_LOCK(sc); 14037b1ce13SDoug Ambrisko if (poll_events & (POLLIN | POLLRDNORM)) { 141d72a0786SJohn Baldwin if (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) 14237b1ce13SDoug Ambrisko revents |= poll_events & (POLLIN | POLLRDNORM); 143d72a0786SJohn Baldwin if (dev->ipmi_requests == 0) 14437b1ce13SDoug Ambrisko revents |= POLLERR; 14537b1ce13SDoug Ambrisko } 14637b1ce13SDoug Ambrisko 14737b1ce13SDoug Ambrisko if (revents == 0) { 14837b1ce13SDoug Ambrisko if (poll_events & (POLLIN | POLLRDNORM)) 149d72a0786SJohn Baldwin selrecord(td, &dev->ipmi_select); 150d72a0786SJohn Baldwin } 151d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 152d72a0786SJohn Baldwin 153d72a0786SJohn Baldwin return (revents); 15437b1ce13SDoug Ambrisko } 15537b1ce13SDoug Ambrisko 156d72a0786SJohn Baldwin static void 157d72a0786SJohn Baldwin ipmi_purge_completed_requests(struct ipmi_device *dev) 158d72a0786SJohn Baldwin { 159d72a0786SJohn Baldwin struct ipmi_request *req; 160d72a0786SJohn Baldwin 161d72a0786SJohn Baldwin while (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) { 162d72a0786SJohn Baldwin req = TAILQ_FIRST(&dev->ipmi_completed_requests); 163d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, req, ir_link); 164d72a0786SJohn Baldwin dev->ipmi_requests--; 165d72a0786SJohn Baldwin ipmi_free_request(req); 166d72a0786SJohn Baldwin } 16737b1ce13SDoug Ambrisko } 16837b1ce13SDoug Ambrisko 169943bebd2SJohn Baldwin static void 170943bebd2SJohn Baldwin ipmi_dtor(void *arg) 17137b1ce13SDoug Ambrisko { 172d72a0786SJohn Baldwin struct ipmi_request *req, *nreq; 173d72a0786SJohn Baldwin struct ipmi_device *dev; 17437b1ce13SDoug Ambrisko struct ipmi_softc *sc; 17537b1ce13SDoug Ambrisko 176943bebd2SJohn Baldwin dev = arg; 177d72a0786SJohn Baldwin sc = dev->ipmi_softc; 17837b1ce13SDoug Ambrisko 179d72a0786SJohn Baldwin IPMI_LOCK(sc); 180d72a0786SJohn Baldwin if (dev->ipmi_requests) { 181d72a0786SJohn Baldwin /* Throw away any pending requests for this device. */ 182d72a0786SJohn Baldwin TAILQ_FOREACH_SAFE(req, &sc->ipmi_pending_requests, ir_link, 183d72a0786SJohn Baldwin nreq) { 184d72a0786SJohn Baldwin if (req->ir_owner == dev) { 185d72a0786SJohn Baldwin TAILQ_REMOVE(&sc->ipmi_pending_requests, req, 186d72a0786SJohn Baldwin ir_link); 187d72a0786SJohn Baldwin dev->ipmi_requests--; 188d72a0786SJohn Baldwin ipmi_free_request(req); 189d72a0786SJohn Baldwin } 190d72a0786SJohn Baldwin } 19137b1ce13SDoug Ambrisko 192d72a0786SJohn Baldwin /* Throw away any pending completed requests for this device. */ 193d72a0786SJohn Baldwin ipmi_purge_completed_requests(dev); 194d72a0786SJohn Baldwin 195d72a0786SJohn Baldwin /* 196d72a0786SJohn Baldwin * If we still have outstanding requests, they must be stuck 197d72a0786SJohn Baldwin * in an interface driver, so wait for those to drain. 198d72a0786SJohn Baldwin */ 199d72a0786SJohn Baldwin dev->ipmi_closing = 1; 200d72a0786SJohn Baldwin while (dev->ipmi_requests > 0) { 201c869aa71SJohn Baldwin msleep(&dev->ipmi_requests, &sc->ipmi_requests_lock, 202c869aa71SJohn Baldwin PWAIT, "ipmidrain", 0); 203d72a0786SJohn Baldwin ipmi_purge_completed_requests(dev); 204d72a0786SJohn Baldwin } 205d72a0786SJohn Baldwin } 206943bebd2SJohn Baldwin sc->ipmi_opened--; 207d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 208d72a0786SJohn Baldwin 209d72a0786SJohn Baldwin /* Cleanup. */ 210d72a0786SJohn Baldwin free(dev, M_IPMI); 21137b1ce13SDoug Ambrisko } 21237b1ce13SDoug Ambrisko 21337b1ce13SDoug Ambrisko #ifdef IPMB 21437b1ce13SDoug Ambrisko static int 21537b1ce13SDoug Ambrisko ipmi_ipmb_checksum(u_char *data, int len) 21637b1ce13SDoug Ambrisko { 21737b1ce13SDoug Ambrisko u_char sum = 0; 21837b1ce13SDoug Ambrisko 21937b1ce13SDoug Ambrisko for (; len; len--) { 22037b1ce13SDoug Ambrisko sum += *data++; 22137b1ce13SDoug Ambrisko } 222d72a0786SJohn Baldwin return (-sum); 22337b1ce13SDoug Ambrisko } 22437b1ce13SDoug Ambrisko 225d72a0786SJohn Baldwin /* XXX: Needs work */ 22637b1ce13SDoug Ambrisko static int 22737b1ce13SDoug Ambrisko ipmi_ipmb_send_message(device_t dev, u_char channel, u_char netfn, 22837b1ce13SDoug Ambrisko u_char command, u_char seq, u_char *data, int data_len) 22937b1ce13SDoug Ambrisko { 23037b1ce13SDoug Ambrisko struct ipmi_softc *sc = device_get_softc(dev); 231d72a0786SJohn Baldwin struct ipmi_request *req; 23237b1ce13SDoug Ambrisko u_char slave_addr = 0x52; 233d72a0786SJohn Baldwin int error; 23437b1ce13SDoug Ambrisko 235c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 236d72a0786SJohn Baldwin IPMI_SEND_MSG, data_len + 8, 0); 237d72a0786SJohn Baldwin req->ir_request[0] = channel; 238d72a0786SJohn Baldwin req->ir_request[1] = slave_addr; 239d72a0786SJohn Baldwin req->ir_request[2] = IPMI_ADDR(netfn, 0); 240d72a0786SJohn Baldwin req->ir_request[3] = ipmi_ipmb_checksum(&req->ir_request[1], 2); 241d72a0786SJohn Baldwin req->ir_request[4] = sc->ipmi_address; 242d72a0786SJohn Baldwin req->ir_request[5] = IPMI_ADDR(seq, sc->ipmi_lun); 243d72a0786SJohn Baldwin req->ir_request[6] = command; 24437b1ce13SDoug Ambrisko 245d72a0786SJohn Baldwin bcopy(data, &req->ir_request[7], data_len); 246d72a0786SJohn Baldwin temp[data_len + 7] = ipmi_ipmb_checksum(&req->ir_request[4], 247d72a0786SJohn Baldwin data_len + 3); 24837b1ce13SDoug Ambrisko 249d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req); 250d72a0786SJohn Baldwin error = req->ir_error; 25137b1ce13SDoug Ambrisko 252d72a0786SJohn Baldwin return (error); 25337b1ce13SDoug Ambrisko } 25437b1ce13SDoug Ambrisko 25537b1ce13SDoug Ambrisko static int 256d72a0786SJohn Baldwin ipmi_handle_attn(struct ipmi_softc *sc) 25737b1ce13SDoug Ambrisko { 258d72a0786SJohn Baldwin struct ipmi_request *req; 25937b1ce13SDoug Ambrisko int error; 26037b1ce13SDoug Ambrisko 26137b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "BMC has a message\n"); 262c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 263d72a0786SJohn Baldwin IPMI_GET_MSG_FLAGS, 0, 1); 26437b1ce13SDoug Ambrisko 265d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req); 266d72a0786SJohn Baldwin 267d72a0786SJohn Baldwin if (req->ir_error == 0 && req->ir_compcode == 0) { 268d72a0786SJohn Baldwin if (req->ir_reply[0] & IPMI_MSG_BUFFER_FULL) { 26937b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "message buffer full"); 27037b1ce13SDoug Ambrisko } 271d72a0786SJohn Baldwin if (req->ir_reply[0] & IPMI_WDT_PRE_TIMEOUT) { 27237b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, 27337b1ce13SDoug Ambrisko "watchdog about to go off"); 27437b1ce13SDoug Ambrisko } 275d72a0786SJohn Baldwin if (req->ir_reply[0] & IPMI_MSG_AVAILABLE) { 276c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, 277d72a0786SJohn Baldwin IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG, 0, 278d72a0786SJohn Baldwin 16); 27937b1ce13SDoug Ambrisko 28037b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "throw out message "); 28137b1ce13SDoug Ambrisko dump_buf(temp, 16); 28237b1ce13SDoug Ambrisko } 28337b1ce13SDoug Ambrisko } 284d72a0786SJohn Baldwin error = req->ir_error; 285d72a0786SJohn Baldwin 286d72a0786SJohn Baldwin return (error); 287d72a0786SJohn Baldwin } 288d72a0786SJohn Baldwin #endif 289d72a0786SJohn Baldwin 290d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 291d72a0786SJohn Baldwin #define PTRIN(p) ((void *)(uintptr_t)(p)) 292d72a0786SJohn Baldwin #define PTROUT(p) ((uintptr_t)(p)) 29337b1ce13SDoug Ambrisko #endif 29437b1ce13SDoug Ambrisko 29537b1ce13SDoug Ambrisko static int 296d72a0786SJohn Baldwin ipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, 29737b1ce13SDoug Ambrisko int flags, struct thread *td) 29837b1ce13SDoug Ambrisko { 29937b1ce13SDoug Ambrisko struct ipmi_softc *sc; 300d72a0786SJohn Baldwin struct ipmi_device *dev; 301d72a0786SJohn Baldwin struct ipmi_request *kreq; 30237b1ce13SDoug Ambrisko struct ipmi_req *req = (struct ipmi_req *)data; 30337b1ce13SDoug Ambrisko struct ipmi_recv *recv = (struct ipmi_recv *)data; 30437b1ce13SDoug Ambrisko struct ipmi_addr addr; 305d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 306d72a0786SJohn Baldwin struct ipmi_req32 *req32 = (struct ipmi_req32 *)data; 307d72a0786SJohn Baldwin struct ipmi_recv32 *recv32 = (struct ipmi_recv32 *)data; 308d72a0786SJohn Baldwin union { 309d72a0786SJohn Baldwin struct ipmi_req req; 310d72a0786SJohn Baldwin struct ipmi_recv recv; 311d72a0786SJohn Baldwin } thunk32; 312d72a0786SJohn Baldwin #endif 31337b1ce13SDoug Ambrisko int error, len; 31437b1ce13SDoug Ambrisko 315943bebd2SJohn Baldwin error = devfs_get_cdevpriv((void **)&dev); 316943bebd2SJohn Baldwin if (error) 317943bebd2SJohn Baldwin return (error); 318943bebd2SJohn Baldwin 319943bebd2SJohn Baldwin sc = cdev->si_drv1; 320d72a0786SJohn Baldwin 321d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 322d72a0786SJohn Baldwin /* Convert 32-bit structures to native. */ 323d72a0786SJohn Baldwin switch (cmd) { 324d72a0786SJohn Baldwin case IPMICTL_SEND_COMMAND_32: 325d72a0786SJohn Baldwin req = &thunk32.req; 326d72a0786SJohn Baldwin req->addr = PTRIN(req32->addr); 327d72a0786SJohn Baldwin req->addr_len = req32->addr_len; 328d72a0786SJohn Baldwin req->msgid = req32->msgid; 329d72a0786SJohn Baldwin req->msg.netfn = req32->msg.netfn; 330d72a0786SJohn Baldwin req->msg.cmd = req32->msg.cmd; 331d72a0786SJohn Baldwin req->msg.data_len = req32->msg.data_len; 332d72a0786SJohn Baldwin req->msg.data = PTRIN(req32->msg.data); 333d72a0786SJohn Baldwin break; 334d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 335d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 336d72a0786SJohn Baldwin recv = &thunk32.recv; 337d72a0786SJohn Baldwin recv->addr = PTRIN(recv32->addr); 338d72a0786SJohn Baldwin recv->addr_len = recv32->addr_len; 339d72a0786SJohn Baldwin recv->msg.data_len = recv32->msg.data_len; 340d72a0786SJohn Baldwin recv->msg.data = PTRIN(recv32->msg.data); 341d72a0786SJohn Baldwin break; 342d72a0786SJohn Baldwin } 343d72a0786SJohn Baldwin #endif 34437b1ce13SDoug Ambrisko 34537b1ce13SDoug Ambrisko switch (cmd) { 346d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 347d72a0786SJohn Baldwin case IPMICTL_SEND_COMMAND_32: 348d72a0786SJohn Baldwin #endif 34937b1ce13SDoug Ambrisko case IPMICTL_SEND_COMMAND: 350d72a0786SJohn Baldwin /* 351d72a0786SJohn Baldwin * XXX: Need to add proper handling of this. 352d72a0786SJohn Baldwin */ 35337b1ce13SDoug Ambrisko error = copyin(req->addr, &addr, sizeof(addr)); 354d72a0786SJohn Baldwin if (error) 355d72a0786SJohn Baldwin return (error); 356d72a0786SJohn Baldwin 357d72a0786SJohn Baldwin IPMI_LOCK(sc); 358d72a0786SJohn Baldwin /* clear out old stuff in queue of stuff done */ 359d72a0786SJohn Baldwin /* XXX: This seems odd. */ 360d72a0786SJohn Baldwin while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))) { 361d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, 362d72a0786SJohn Baldwin ir_link); 363d72a0786SJohn Baldwin dev->ipmi_requests--; 364d72a0786SJohn Baldwin ipmi_free_request(kreq); 36537b1ce13SDoug Ambrisko } 366d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 367d72a0786SJohn Baldwin 368d72a0786SJohn Baldwin kreq = ipmi_alloc_request(dev, req->msgid, 369d72a0786SJohn Baldwin IPMI_ADDR(req->msg.netfn, 0), req->msg.cmd, 370d72a0786SJohn Baldwin req->msg.data_len, IPMI_MAX_RX); 371d72a0786SJohn Baldwin error = copyin(req->msg.data, kreq->ir_request, 37237b1ce13SDoug Ambrisko req->msg.data_len); 373d72a0786SJohn Baldwin if (error) { 374d72a0786SJohn Baldwin ipmi_free_request(kreq); 375d72a0786SJohn Baldwin return (error); 37637b1ce13SDoug Ambrisko } 377d72a0786SJohn Baldwin IPMI_LOCK(sc); 378d72a0786SJohn Baldwin dev->ipmi_requests++; 379d72a0786SJohn Baldwin error = sc->ipmi_enqueue_request(sc, kreq); 380d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 381d72a0786SJohn Baldwin if (error) 382d72a0786SJohn Baldwin return (error); 383d72a0786SJohn Baldwin break; 384d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 385d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 386d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 387d72a0786SJohn Baldwin #endif 38837b1ce13SDoug Ambrisko case IPMICTL_RECEIVE_MSG_TRUNC: 38937b1ce13SDoug Ambrisko case IPMICTL_RECEIVE_MSG: 39037b1ce13SDoug Ambrisko error = copyin(recv->addr, &addr, sizeof(addr)); 391d72a0786SJohn Baldwin if (error) 392d72a0786SJohn Baldwin return (error); 393d72a0786SJohn Baldwin 394d72a0786SJohn Baldwin IPMI_LOCK(sc); 395d72a0786SJohn Baldwin kreq = TAILQ_FIRST(&dev->ipmi_completed_requests); 396d72a0786SJohn Baldwin if (kreq == NULL) { 397d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 398d72a0786SJohn Baldwin return (EAGAIN); 39937b1ce13SDoug Ambrisko } 400d72a0786SJohn Baldwin addr.channel = IPMI_BMC_CHANNEL; 401d72a0786SJohn Baldwin /* XXX */ 402d72a0786SJohn Baldwin recv->recv_type = IPMI_RESPONSE_RECV_TYPE; 403d72a0786SJohn Baldwin recv->msgid = kreq->ir_msgid; 404d72a0786SJohn Baldwin recv->msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2; 405d72a0786SJohn Baldwin recv->msg.cmd = kreq->ir_command; 406d72a0786SJohn Baldwin error = kreq->ir_error; 407d72a0786SJohn Baldwin if (error) { 408d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, 409d72a0786SJohn Baldwin ir_link); 410d72a0786SJohn Baldwin dev->ipmi_requests--; 411d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 412d72a0786SJohn Baldwin ipmi_free_request(kreq); 413d72a0786SJohn Baldwin return (error); 414d72a0786SJohn Baldwin } 415d72a0786SJohn Baldwin len = kreq->ir_replylen + 1; 416d72a0786SJohn Baldwin if (recv->msg.data_len < len && 417d72a0786SJohn Baldwin (cmd == IPMICTL_RECEIVE_MSG 418d72a0786SJohn Baldwin #ifdef IPMICTL_RECEIVE_MSG_32 419c12dbd1dSDavid E. O'Brien || cmd == IPMICTL_RECEIVE_MSG_32 420d72a0786SJohn Baldwin #endif 421d72a0786SJohn Baldwin )) { 422d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 423d72a0786SJohn Baldwin return (EMSGSIZE); 424d72a0786SJohn Baldwin } 425d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link); 426d72a0786SJohn Baldwin dev->ipmi_requests--; 427d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 42837b1ce13SDoug Ambrisko len = min(recv->msg.data_len, len); 42937b1ce13SDoug Ambrisko recv->msg.data_len = len; 43037b1ce13SDoug Ambrisko error = copyout(&addr, recv->addr,sizeof(addr)); 43137b1ce13SDoug Ambrisko if (error == 0) 432d72a0786SJohn Baldwin error = copyout(&kreq->ir_compcode, recv->msg.data, 1); 433d72a0786SJohn Baldwin if (error == 0) 434d72a0786SJohn Baldwin error = copyout(kreq->ir_reply, recv->msg.data + 1, 435d72a0786SJohn Baldwin len - 1); 436d72a0786SJohn Baldwin ipmi_free_request(kreq); 437d72a0786SJohn Baldwin if (error) 438d72a0786SJohn Baldwin return (error); 439d72a0786SJohn Baldwin break; 44037b1ce13SDoug Ambrisko case IPMICTL_SET_MY_ADDRESS_CMD: 441d72a0786SJohn Baldwin IPMI_LOCK(sc); 442d72a0786SJohn Baldwin dev->ipmi_address = *(int*)data; 443d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 444d72a0786SJohn Baldwin break; 44537b1ce13SDoug Ambrisko case IPMICTL_GET_MY_ADDRESS_CMD: 446d72a0786SJohn Baldwin IPMI_LOCK(sc); 447d72a0786SJohn Baldwin *(int*)data = dev->ipmi_address; 448d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 449d72a0786SJohn Baldwin break; 45037b1ce13SDoug Ambrisko case IPMICTL_SET_MY_LUN_CMD: 451d72a0786SJohn Baldwin IPMI_LOCK(sc); 452d72a0786SJohn Baldwin dev->ipmi_lun = *(int*)data & 0x3; 453d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 454d72a0786SJohn Baldwin break; 45537b1ce13SDoug Ambrisko case IPMICTL_GET_MY_LUN_CMD: 456d72a0786SJohn Baldwin IPMI_LOCK(sc); 457d72a0786SJohn Baldwin *(int*)data = dev->ipmi_lun; 458d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 459d72a0786SJohn Baldwin break; 46037b1ce13SDoug Ambrisko case IPMICTL_SET_GETS_EVENTS_CMD: 46137b1ce13SDoug Ambrisko /* 46237b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, 46337b1ce13SDoug Ambrisko "IPMICTL_SET_GETS_EVENTS_CMD NA\n"); 46437b1ce13SDoug Ambrisko */ 465d72a0786SJohn Baldwin break; 46637b1ce13SDoug Ambrisko case IPMICTL_REGISTER_FOR_CMD: 46737b1ce13SDoug Ambrisko case IPMICTL_UNREGISTER_FOR_CMD: 468d72a0786SJohn Baldwin return (EOPNOTSUPP); 469d72a0786SJohn Baldwin default: 47037b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "Unknown IOCTL %lX\n", cmd); 471d72a0786SJohn Baldwin return (ENOIOCTL); 47237b1ce13SDoug Ambrisko } 47337b1ce13SDoug Ambrisko 474d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 475d72a0786SJohn Baldwin /* Update changed fields in 32-bit structures. */ 476d72a0786SJohn Baldwin switch (cmd) { 477d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 478d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 479d72a0786SJohn Baldwin recv32->recv_type = recv->recv_type; 480d72a0786SJohn Baldwin recv32->msgid = recv->msgid; 481d72a0786SJohn Baldwin recv32->msg.netfn = recv->msg.netfn; 482d72a0786SJohn Baldwin recv32->msg.cmd = recv->msg.cmd; 483d72a0786SJohn Baldwin recv32->msg.data_len = recv->msg.data_len; 48437b1ce13SDoug Ambrisko break; 48537b1ce13SDoug Ambrisko } 486d72a0786SJohn Baldwin #endif 487d72a0786SJohn Baldwin return (0); 48837b1ce13SDoug Ambrisko } 48937b1ce13SDoug Ambrisko 490d72a0786SJohn Baldwin /* 491d72a0786SJohn Baldwin * Request management. 492d72a0786SJohn Baldwin */ 49337b1ce13SDoug Ambrisko 494c869aa71SJohn Baldwin static __inline void 495c869aa71SJohn Baldwin ipmi_init_request(struct ipmi_request *req, struct ipmi_device *dev, long msgid, 496c869aa71SJohn Baldwin uint8_t addr, uint8_t command, size_t requestlen, size_t replylen) 497d72a0786SJohn Baldwin { 498d72a0786SJohn Baldwin 499d72a0786SJohn Baldwin req->ir_owner = dev; 500d72a0786SJohn Baldwin req->ir_msgid = msgid; 501d72a0786SJohn Baldwin req->ir_addr = addr; 502d72a0786SJohn Baldwin req->ir_command = command; 503d72a0786SJohn Baldwin if (requestlen) { 504d72a0786SJohn Baldwin req->ir_request = (char *)&req[1]; 505d72a0786SJohn Baldwin req->ir_requestlen = requestlen; 506d72a0786SJohn Baldwin } 507d72a0786SJohn Baldwin if (replylen) { 508d72a0786SJohn Baldwin req->ir_reply = (char *)&req[1] + requestlen; 509d72a0786SJohn Baldwin req->ir_replybuflen = replylen; 510d72a0786SJohn Baldwin } 511c869aa71SJohn Baldwin } 512c869aa71SJohn Baldwin 513c869aa71SJohn Baldwin /* Allocate a new request with request and reply buffers. */ 514c869aa71SJohn Baldwin struct ipmi_request * 515c869aa71SJohn Baldwin ipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr, 516c869aa71SJohn Baldwin uint8_t command, size_t requestlen, size_t replylen) 517c869aa71SJohn Baldwin { 518c869aa71SJohn Baldwin struct ipmi_request *req; 519c869aa71SJohn Baldwin 520c869aa71SJohn Baldwin req = malloc(sizeof(struct ipmi_request) + requestlen + replylen, 521c869aa71SJohn Baldwin M_IPMI, M_WAITOK | M_ZERO); 522c869aa71SJohn Baldwin ipmi_init_request(req, dev, msgid, addr, command, requestlen, replylen); 523d72a0786SJohn Baldwin return (req); 52437b1ce13SDoug Ambrisko } 52537b1ce13SDoug Ambrisko 526d72a0786SJohn Baldwin /* Free a request no longer in use. */ 527d72a0786SJohn Baldwin void 528d72a0786SJohn Baldwin ipmi_free_request(struct ipmi_request *req) 529d72a0786SJohn Baldwin { 53037b1ce13SDoug Ambrisko 531d72a0786SJohn Baldwin free(req, M_IPMI); 53237b1ce13SDoug Ambrisko } 53337b1ce13SDoug Ambrisko 534d72a0786SJohn Baldwin /* Store a processed request on the appropriate completion queue. */ 535d72a0786SJohn Baldwin void 536d72a0786SJohn Baldwin ipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req) 537d72a0786SJohn Baldwin { 538d72a0786SJohn Baldwin struct ipmi_device *dev; 53937b1ce13SDoug Ambrisko 540d72a0786SJohn Baldwin IPMI_LOCK_ASSERT(sc); 541d72a0786SJohn Baldwin 542d72a0786SJohn Baldwin /* 543d72a0786SJohn Baldwin * Anonymous requests (from inside the driver) always have a 544d72a0786SJohn Baldwin * waiter that we awaken. 545d72a0786SJohn Baldwin */ 546d72a0786SJohn Baldwin if (req->ir_owner == NULL) 547d72a0786SJohn Baldwin wakeup(req); 548d72a0786SJohn Baldwin else { 549d72a0786SJohn Baldwin dev = req->ir_owner; 550d72a0786SJohn Baldwin TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link); 551d72a0786SJohn Baldwin selwakeup(&dev->ipmi_select); 552d72a0786SJohn Baldwin if (dev->ipmi_closing) 553d72a0786SJohn Baldwin wakeup(&dev->ipmi_requests); 554d72a0786SJohn Baldwin } 55537b1ce13SDoug Ambrisko } 55637b1ce13SDoug Ambrisko 557c869aa71SJohn Baldwin /* Perform an internal driver request. */ 55837b1ce13SDoug Ambrisko int 559d72a0786SJohn Baldwin ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, 560d72a0786SJohn Baldwin int timo) 561d72a0786SJohn Baldwin { 56237b1ce13SDoug Ambrisko 563c869aa71SJohn Baldwin return (sc->ipmi_driver_request(sc, req, timo)); 56437b1ce13SDoug Ambrisko } 56537b1ce13SDoug Ambrisko 566d72a0786SJohn Baldwin /* 567d72a0786SJohn Baldwin * Helper routine for polled system interfaces that use 568d72a0786SJohn Baldwin * ipmi_polled_enqueue_request() to queue requests. This request 569d72a0786SJohn Baldwin * waits until there is a pending request and then returns the first 570d72a0786SJohn Baldwin * request. If the driver is shutting down, it returns NULL. 571d72a0786SJohn Baldwin */ 572d72a0786SJohn Baldwin struct ipmi_request * 573d72a0786SJohn Baldwin ipmi_dequeue_request(struct ipmi_softc *sc) 574d72a0786SJohn Baldwin { 575d72a0786SJohn Baldwin struct ipmi_request *req; 57637b1ce13SDoug Ambrisko 577d72a0786SJohn Baldwin IPMI_LOCK_ASSERT(sc); 57837b1ce13SDoug Ambrisko 579d72a0786SJohn Baldwin while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests)) 580c869aa71SJohn Baldwin cv_wait(&sc->ipmi_request_added, &sc->ipmi_requests_lock); 581d72a0786SJohn Baldwin if (sc->ipmi_detaching) 582d72a0786SJohn Baldwin return (NULL); 58337b1ce13SDoug Ambrisko 584d72a0786SJohn Baldwin req = TAILQ_FIRST(&sc->ipmi_pending_requests); 585d72a0786SJohn Baldwin TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); 586d72a0786SJohn Baldwin return (req); 58737b1ce13SDoug Ambrisko } 58837b1ce13SDoug Ambrisko 589d72a0786SJohn Baldwin /* Default implementation of ipmi_enqueue_request() for polled interfaces. */ 590d72a0786SJohn Baldwin int 591d72a0786SJohn Baldwin ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req) 592d72a0786SJohn Baldwin { 59337b1ce13SDoug Ambrisko 5945283d39bSJohn Baldwin IPMI_LOCK_ASSERT(sc); 5955283d39bSJohn Baldwin 596d72a0786SJohn Baldwin TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link); 597d72a0786SJohn Baldwin cv_signal(&sc->ipmi_request_added); 598d72a0786SJohn Baldwin return (0); 59937b1ce13SDoug Ambrisko } 60037b1ce13SDoug Ambrisko 60137b1ce13SDoug Ambrisko /* 60237b1ce13SDoug Ambrisko * Watchdog event handler. 60337b1ce13SDoug Ambrisko */ 60437b1ce13SDoug Ambrisko 605a46a1e76SRuslan Ermilov static int 606a46a1e76SRuslan Ermilov ipmi_set_watchdog(struct ipmi_softc *sc, unsigned int sec) 607d72a0786SJohn Baldwin { 608d72a0786SJohn Baldwin struct ipmi_request *req; 609d72a0786SJohn Baldwin int error; 61037b1ce13SDoug Ambrisko 611a46a1e76SRuslan Ermilov if (sec > 0xffff / 10) 612a46a1e76SRuslan Ermilov return (EINVAL); 613a46a1e76SRuslan Ermilov 614c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 615d72a0786SJohn Baldwin IPMI_SET_WDOG, 6, 0); 61637b1ce13SDoug Ambrisko 61737b1ce13SDoug Ambrisko if (sec) { 618d72a0786SJohn Baldwin req->ir_request[0] = IPMI_SET_WD_TIMER_DONT_STOP 61937b1ce13SDoug Ambrisko | IPMI_SET_WD_TIMER_SMS_OS; 620d72a0786SJohn Baldwin req->ir_request[1] = IPMI_SET_WD_ACTION_RESET; 621d72a0786SJohn Baldwin req->ir_request[2] = 0; 622d72a0786SJohn Baldwin req->ir_request[3] = 0; /* Timer use */ 623d72a0786SJohn Baldwin req->ir_request[4] = (sec * 10) & 0xff; 624a46a1e76SRuslan Ermilov req->ir_request[5] = (sec * 10) >> 8; 62537b1ce13SDoug Ambrisko } else { 626d72a0786SJohn Baldwin req->ir_request[0] = IPMI_SET_WD_TIMER_SMS_OS; 627d72a0786SJohn Baldwin req->ir_request[1] = 0; 628d72a0786SJohn Baldwin req->ir_request[2] = 0; 629d72a0786SJohn Baldwin req->ir_request[3] = 0; /* Timer use */ 630d72a0786SJohn Baldwin req->ir_request[4] = 0; 631d72a0786SJohn Baldwin req->ir_request[5] = 0; 63237b1ce13SDoug Ambrisko } 63337b1ce13SDoug Ambrisko 634d72a0786SJohn Baldwin error = ipmi_submit_driver_request(sc, req, 0); 635d72a0786SJohn Baldwin if (error) 636d72a0786SJohn Baldwin device_printf(sc->ipmi_dev, "Failed to set watchdog\n"); 637a46a1e76SRuslan Ermilov else if (sec) { 638c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 639d72a0786SJohn Baldwin IPMI_RESET_WDOG, 0, 0); 64037b1ce13SDoug Ambrisko 641d72a0786SJohn Baldwin error = ipmi_submit_driver_request(sc, req, 0); 642d72a0786SJohn Baldwin if (error) 643d72a0786SJohn Baldwin device_printf(sc->ipmi_dev, 644d72a0786SJohn Baldwin "Failed to reset watchdog\n"); 64537b1ce13SDoug Ambrisko } 64637b1ce13SDoug Ambrisko 647a46a1e76SRuslan Ermilov return (error); 64837b1ce13SDoug Ambrisko /* 649d72a0786SJohn Baldwin dump_watchdog(sc); 65037b1ce13SDoug Ambrisko */ 65137b1ce13SDoug Ambrisko } 65237b1ce13SDoug Ambrisko 65337b1ce13SDoug Ambrisko static void 65437b1ce13SDoug Ambrisko ipmi_wd_event(void *arg, unsigned int cmd, int *error) 65537b1ce13SDoug Ambrisko { 65637b1ce13SDoug Ambrisko struct ipmi_softc *sc = arg; 65737b1ce13SDoug Ambrisko unsigned int timeout; 658a46a1e76SRuslan Ermilov int e; 65937b1ce13SDoug Ambrisko 660a9b3c1bfSGleb Smirnoff if (dumping) 661a9b3c1bfSGleb Smirnoff return; 662a9b3c1bfSGleb Smirnoff 66337b1ce13SDoug Ambrisko cmd &= WD_INTERVAL; 6649079fff5SNick Hibma if (cmd > 0 && cmd <= 63) { 665a46a1e76SRuslan Ermilov timeout = ((uint64_t)1 << cmd) / 1000000000; 666a46a1e76SRuslan Ermilov if (timeout == 0) 667a46a1e76SRuslan Ermilov timeout = 1; 668a46a1e76SRuslan Ermilov e = ipmi_set_watchdog(sc, timeout); 6691710852eSJohn Baldwin if (e == 0) { 67037b1ce13SDoug Ambrisko *error = 0; 6711710852eSJohn Baldwin sc->ipmi_watchdog_active = 1; 6721710852eSJohn Baldwin } else 673a46a1e76SRuslan Ermilov (void)ipmi_set_watchdog(sc, 0); 6741710852eSJohn Baldwin } else if (atomic_readandclear_int(&sc->ipmi_watchdog_active) != 0) { 675a46a1e76SRuslan Ermilov e = ipmi_set_watchdog(sc, 0); 676a46a1e76SRuslan Ermilov if (e != 0 && cmd == 0) 677a46a1e76SRuslan Ermilov *error = EOPNOTSUPP; 6789079fff5SNick Hibma } 67937b1ce13SDoug Ambrisko } 68037b1ce13SDoug Ambrisko 681d72a0786SJohn Baldwin static void 682d72a0786SJohn Baldwin ipmi_startup(void *arg) 683d72a0786SJohn Baldwin { 684d72a0786SJohn Baldwin struct ipmi_softc *sc = arg; 685d72a0786SJohn Baldwin struct ipmi_request *req; 686d72a0786SJohn Baldwin device_t dev; 687d72a0786SJohn Baldwin int error, i; 688d72a0786SJohn Baldwin 689d72a0786SJohn Baldwin config_intrhook_disestablish(&sc->ipmi_ich); 690d72a0786SJohn Baldwin dev = sc->ipmi_dev; 691d72a0786SJohn Baldwin 692d72a0786SJohn Baldwin /* Initialize interface-independent state. */ 693c869aa71SJohn Baldwin mtx_init(&sc->ipmi_requests_lock, "ipmi requests", NULL, MTX_DEF); 694c869aa71SJohn Baldwin mtx_init(&sc->ipmi_io_lock, "ipmi io", NULL, MTX_DEF); 695d72a0786SJohn Baldwin cv_init(&sc->ipmi_request_added, "ipmireq"); 696d72a0786SJohn Baldwin TAILQ_INIT(&sc->ipmi_pending_requests); 697d72a0786SJohn Baldwin 698d72a0786SJohn Baldwin /* Initialize interface-dependent state. */ 699d72a0786SJohn Baldwin error = sc->ipmi_startup(sc); 700d72a0786SJohn Baldwin if (error) { 701d72a0786SJohn Baldwin device_printf(dev, "Failed to initialize interface: %d\n", 702d72a0786SJohn Baldwin error); 703d72a0786SJohn Baldwin return; 704d72a0786SJohn Baldwin } 705d72a0786SJohn Baldwin 706d72a0786SJohn Baldwin /* Send a GET_DEVICE_ID request. */ 707c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 708d72a0786SJohn Baldwin IPMI_GET_DEVICE_ID, 0, 15); 709d72a0786SJohn Baldwin 710d72a0786SJohn Baldwin error = ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); 711d72a0786SJohn Baldwin if (error == EWOULDBLOCK) { 712d72a0786SJohn Baldwin device_printf(dev, "Timed out waiting for GET_DEVICE_ID\n"); 713d72a0786SJohn Baldwin return; 714d72a0786SJohn Baldwin } else if (error) { 715d72a0786SJohn Baldwin device_printf(dev, "Failed GET_DEVICE_ID: %d\n", error); 716d72a0786SJohn Baldwin return; 717d72a0786SJohn Baldwin } else if (req->ir_compcode != 0) { 718d72a0786SJohn Baldwin device_printf(dev, 719d72a0786SJohn Baldwin "Bad completion code for GET_DEVICE_ID: %d\n", 720d72a0786SJohn Baldwin req->ir_compcode); 721d72a0786SJohn Baldwin return; 722d72a0786SJohn Baldwin } else if (req->ir_replylen < 5) { 723d72a0786SJohn Baldwin device_printf(dev, "Short reply for GET_DEVICE_ID: %d\n", 724d72a0786SJohn Baldwin req->ir_replylen); 725d72a0786SJohn Baldwin return; 726d72a0786SJohn Baldwin } 727d72a0786SJohn Baldwin 72814689886SRuslan Ermilov device_printf(dev, "IPMI device rev. %d, firmware rev. %d.%d%d, " 729d72a0786SJohn Baldwin "version %d.%d\n", 730d72a0786SJohn Baldwin req->ir_reply[1] & 0x0f, 73114689886SRuslan Ermilov req->ir_reply[2] & 0x7f, req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f, 732d72a0786SJohn Baldwin req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4); 733d72a0786SJohn Baldwin 734c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 735d72a0786SJohn Baldwin IPMI_CLEAR_FLAGS, 1, 0); 736d72a0786SJohn Baldwin 737d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req, 0); 738d72a0786SJohn Baldwin 739d72a0786SJohn Baldwin /* XXX: Magic numbers */ 740d72a0786SJohn Baldwin if (req->ir_compcode == 0xc0) { 741d72a0786SJohn Baldwin device_printf(dev, "Clear flags is busy\n"); 742d72a0786SJohn Baldwin } 743d72a0786SJohn Baldwin if (req->ir_compcode == 0xc1) { 744d72a0786SJohn Baldwin device_printf(dev, "Clear flags illegal\n"); 745d72a0786SJohn Baldwin } 746d72a0786SJohn Baldwin 747d72a0786SJohn Baldwin for (i = 0; i < 8; i++) { 748c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 749d72a0786SJohn Baldwin IPMI_GET_CHANNEL_INFO, 1, 0); 750d72a0786SJohn Baldwin req->ir_request[0] = i; 751d72a0786SJohn Baldwin 752d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req, 0); 753d72a0786SJohn Baldwin 754c869aa71SJohn Baldwin if (req->ir_compcode != 0) 755d72a0786SJohn Baldwin break; 756d72a0786SJohn Baldwin } 757d72a0786SJohn Baldwin device_printf(dev, "Number of channels %d\n", i); 758d72a0786SJohn Baldwin 759*9662eef5SJohn Baldwin /* 760*9662eef5SJohn Baldwin * Probe for watchdog, but only for backends which support 761*9662eef5SJohn Baldwin * polled driver requests. 762*9662eef5SJohn Baldwin */ 763*9662eef5SJohn Baldwin if (sc->ipmi_driver_requests_polled) { 764c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 765d72a0786SJohn Baldwin IPMI_GET_WDOG, 0, 0); 766d72a0786SJohn Baldwin 767d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req, 0); 768d72a0786SJohn Baldwin 769d72a0786SJohn Baldwin if (req->ir_compcode == 0x00) { 770d72a0786SJohn Baldwin device_printf(dev, "Attached watchdog\n"); 771d72a0786SJohn Baldwin /* register the watchdog event handler */ 772*9662eef5SJohn Baldwin sc->ipmi_watchdog_tag = EVENTHANDLER_REGISTER( 773*9662eef5SJohn Baldwin watchdog_list, ipmi_wd_event, sc, 0); 774*9662eef5SJohn Baldwin } 775d72a0786SJohn Baldwin } 776d72a0786SJohn Baldwin 777943bebd2SJohn Baldwin sc->ipmi_cdev = make_dev(&ipmi_cdevsw, device_get_unit(dev), 778d72a0786SJohn Baldwin UID_ROOT, GID_OPERATOR, 0660, "ipmi%d", device_get_unit(dev)); 779943bebd2SJohn Baldwin if (sc->ipmi_cdev == NULL) { 780d72a0786SJohn Baldwin device_printf(dev, "Failed to create cdev\n"); 781d72a0786SJohn Baldwin return; 782d72a0786SJohn Baldwin } 783943bebd2SJohn Baldwin sc->ipmi_cdev->si_drv1 = sc; 784d72a0786SJohn Baldwin } 785d72a0786SJohn Baldwin 78637b1ce13SDoug Ambrisko int 78737b1ce13SDoug Ambrisko ipmi_attach(device_t dev) 78837b1ce13SDoug Ambrisko { 78937b1ce13SDoug Ambrisko struct ipmi_softc *sc = device_get_softc(dev); 790d72a0786SJohn Baldwin int error; 79137b1ce13SDoug Ambrisko 792d72a0786SJohn Baldwin if (sc->ipmi_irq_res != NULL && sc->ipmi_intr != NULL) { 793d72a0786SJohn Baldwin error = bus_setup_intr(dev, sc->ipmi_irq_res, INTR_TYPE_MISC, 794ef544f63SPaolo Pisati NULL, sc->ipmi_intr, sc, &sc->ipmi_irq); 795d72a0786SJohn Baldwin if (error) { 796d72a0786SJohn Baldwin device_printf(dev, "can't set up interrupt\n"); 797d72a0786SJohn Baldwin return (error); 79837b1ce13SDoug Ambrisko } 79937b1ce13SDoug Ambrisko } 80037b1ce13SDoug Ambrisko 801d72a0786SJohn Baldwin bzero(&sc->ipmi_ich, sizeof(struct intr_config_hook)); 802d72a0786SJohn Baldwin sc->ipmi_ich.ich_func = ipmi_startup; 803d72a0786SJohn Baldwin sc->ipmi_ich.ich_arg = sc; 804d72a0786SJohn Baldwin if (config_intrhook_establish(&sc->ipmi_ich) != 0) { 805d72a0786SJohn Baldwin device_printf(dev, "can't establish configuration hook\n"); 806d72a0786SJohn Baldwin return (ENOMEM); 80737b1ce13SDoug Ambrisko } 80837b1ce13SDoug Ambrisko 80937b1ce13SDoug Ambrisko ipmi_attached = 1; 810d72a0786SJohn Baldwin return (0); 81137b1ce13SDoug Ambrisko } 81237b1ce13SDoug Ambrisko 81337b1ce13SDoug Ambrisko int 81437b1ce13SDoug Ambrisko ipmi_detach(device_t dev) 81537b1ce13SDoug Ambrisko { 81637b1ce13SDoug Ambrisko struct ipmi_softc *sc; 81737b1ce13SDoug Ambrisko 81837b1ce13SDoug Ambrisko sc = device_get_softc(dev); 819d72a0786SJohn Baldwin 820d72a0786SJohn Baldwin /* Fail if there are any open handles. */ 821d72a0786SJohn Baldwin IPMI_LOCK(sc); 822943bebd2SJohn Baldwin if (sc->ipmi_opened) { 823d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 824d72a0786SJohn Baldwin return (EBUSY); 825d72a0786SJohn Baldwin } 826d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 827943bebd2SJohn Baldwin if (sc->ipmi_cdev) 828943bebd2SJohn Baldwin destroy_dev(sc->ipmi_cdev); 829d72a0786SJohn Baldwin 830d72a0786SJohn Baldwin /* Detach from watchdog handling and turn off watchdog. */ 831d72a0786SJohn Baldwin if (sc->ipmi_watchdog_tag) { 832d72a0786SJohn Baldwin EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_watchdog_tag); 833d72a0786SJohn Baldwin ipmi_set_watchdog(sc, 0); 834d72a0786SJohn Baldwin } 835d72a0786SJohn Baldwin 836d72a0786SJohn Baldwin /* XXX: should use shutdown callout I think. */ 837d72a0786SJohn Baldwin /* If the backend uses a kthread, shut it down. */ 838d72a0786SJohn Baldwin IPMI_LOCK(sc); 839d72a0786SJohn Baldwin sc->ipmi_detaching = 1; 840d72a0786SJohn Baldwin if (sc->ipmi_kthread) { 841d72a0786SJohn Baldwin cv_broadcast(&sc->ipmi_request_added); 842c869aa71SJohn Baldwin msleep(sc->ipmi_kthread, &sc->ipmi_requests_lock, 0, 843c869aa71SJohn Baldwin "ipmi_wait", 0); 844d72a0786SJohn Baldwin } 845d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 846d72a0786SJohn Baldwin if (sc->ipmi_irq) 847d72a0786SJohn Baldwin bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); 848d72a0786SJohn Baldwin 849d72a0786SJohn Baldwin ipmi_release_resources(dev); 850c869aa71SJohn Baldwin mtx_destroy(&sc->ipmi_io_lock); 851c869aa71SJohn Baldwin mtx_destroy(&sc->ipmi_requests_lock); 852d72a0786SJohn Baldwin return (0); 853d72a0786SJohn Baldwin } 854d72a0786SJohn Baldwin 855d72a0786SJohn Baldwin void 856d72a0786SJohn Baldwin ipmi_release_resources(device_t dev) 857d72a0786SJohn Baldwin { 858d72a0786SJohn Baldwin struct ipmi_softc *sc; 859d72a0786SJohn Baldwin int i; 860d72a0786SJohn Baldwin 861d72a0786SJohn Baldwin sc = device_get_softc(dev); 862d72a0786SJohn Baldwin if (sc->ipmi_irq) 863d72a0786SJohn Baldwin bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); 864d72a0786SJohn Baldwin if (sc->ipmi_irq_res) 865d72a0786SJohn Baldwin bus_release_resource(dev, SYS_RES_IRQ, sc->ipmi_irq_rid, 866d72a0786SJohn Baldwin sc->ipmi_irq_res); 867d72a0786SJohn Baldwin for (i = 0; i < MAX_RES; i++) 868d72a0786SJohn Baldwin if (sc->ipmi_io_res[i]) 869d72a0786SJohn Baldwin bus_release_resource(dev, sc->ipmi_io_type, 870d72a0786SJohn Baldwin sc->ipmi_io_rid + i, sc->ipmi_io_res[i]); 871d72a0786SJohn Baldwin } 872d72a0786SJohn Baldwin 873d72a0786SJohn Baldwin devclass_t ipmi_devclass; 874d72a0786SJohn Baldwin 875d72a0786SJohn Baldwin /* XXX: Why? */ 876d72a0786SJohn Baldwin static void 877d72a0786SJohn Baldwin ipmi_unload(void *arg) 878d72a0786SJohn Baldwin { 879d72a0786SJohn Baldwin device_t * devs; 880d72a0786SJohn Baldwin int count; 881d72a0786SJohn Baldwin int i; 882d72a0786SJohn Baldwin 883bec0c98eSJohn Baldwin if (devclass_get_devices(ipmi_devclass, &devs, &count) != 0) 884bec0c98eSJohn Baldwin return; 885d72a0786SJohn Baldwin for (i = 0; i < count; i++) 886d72a0786SJohn Baldwin device_delete_child(device_get_parent(devs[i]), devs[i]); 887bec0c98eSJohn Baldwin free(devs, M_TEMP); 888d72a0786SJohn Baldwin } 889d72a0786SJohn Baldwin SYSUNINIT(ipmi_unload, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipmi_unload, NULL); 890d72a0786SJohn Baldwin 89119d61f3fSDoug Ambrisko #ifdef IMPI_DEBUG 89237b1ce13SDoug Ambrisko static void 893d72a0786SJohn Baldwin dump_buf(u_char *data, int len) 894d72a0786SJohn Baldwin { 89537b1ce13SDoug Ambrisko char buf[20]; 89637b1ce13SDoug Ambrisko char line[1024]; 89737b1ce13SDoug Ambrisko char temp[30]; 89837b1ce13SDoug Ambrisko int count = 0; 89937b1ce13SDoug Ambrisko int i=0; 90037b1ce13SDoug Ambrisko 90137b1ce13SDoug Ambrisko printf("Address %p len %d\n", data, len); 90237b1ce13SDoug Ambrisko if (len > 256) 90337b1ce13SDoug Ambrisko len = 256; 90437b1ce13SDoug Ambrisko line[0] = '\000'; 90537b1ce13SDoug Ambrisko for (; len > 0; len--, data++) { 90637b1ce13SDoug Ambrisko sprintf(temp, "%02x ", *data); 90737b1ce13SDoug Ambrisko strcat(line, temp); 90837b1ce13SDoug Ambrisko if (*data >= ' ' && *data <= '~') 90937b1ce13SDoug Ambrisko buf[count] = *data; 91037b1ce13SDoug Ambrisko else if (*data >= 'A' && *data <= 'Z') 91137b1ce13SDoug Ambrisko buf[count] = *data; 91237b1ce13SDoug Ambrisko else 91337b1ce13SDoug Ambrisko buf[count] = '.'; 91437b1ce13SDoug Ambrisko if (++count == 16) { 91537b1ce13SDoug Ambrisko buf[count] = '\000'; 91637b1ce13SDoug Ambrisko count = 0; 91737b1ce13SDoug Ambrisko printf(" %3x %s %s\n", i, line, buf); 91837b1ce13SDoug Ambrisko i+=16; 91937b1ce13SDoug Ambrisko line[0] = '\000'; 92037b1ce13SDoug Ambrisko } 92137b1ce13SDoug Ambrisko } 92237b1ce13SDoug Ambrisko buf[count] = '\000'; 92337b1ce13SDoug Ambrisko 92437b1ce13SDoug Ambrisko for (; count != 16; count++) { 92537b1ce13SDoug Ambrisko strcat(line, " "); 92637b1ce13SDoug Ambrisko } 92737b1ce13SDoug Ambrisko printf(" %3x %s %s\n", i, line, buf); 92837b1ce13SDoug Ambrisko } 92937b1ce13SDoug Ambrisko #endif 930