137b1ce13SDoug Ambrisko /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 437b1ce13SDoug Ambrisko * Copyright (c) 2006 IronPort Systems Inc. <ambrisko@ironport.com> 537b1ce13SDoug Ambrisko * All rights reserved. 637b1ce13SDoug Ambrisko * 737b1ce13SDoug Ambrisko * Redistribution and use in source and binary forms, with or without 837b1ce13SDoug Ambrisko * modification, are permitted provided that the following conditions 937b1ce13SDoug Ambrisko * are met: 1037b1ce13SDoug Ambrisko * 1. Redistributions of source code must retain the above copyright 1137b1ce13SDoug Ambrisko * notice, this list of conditions and the following disclaimer. 1237b1ce13SDoug Ambrisko * 2. Redistributions in binary form must reproduce the above copyright 1337b1ce13SDoug Ambrisko * notice, this list of conditions and the following disclaimer in the 1437b1ce13SDoug Ambrisko * documentation and/or other materials provided with the distribution. 1537b1ce13SDoug Ambrisko * 1637b1ce13SDoug Ambrisko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1737b1ce13SDoug Ambrisko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1837b1ce13SDoug Ambrisko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1937b1ce13SDoug Ambrisko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2037b1ce13SDoug Ambrisko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2137b1ce13SDoug Ambrisko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2237b1ce13SDoug Ambrisko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2337b1ce13SDoug Ambrisko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2437b1ce13SDoug Ambrisko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2537b1ce13SDoug Ambrisko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2637b1ce13SDoug Ambrisko * SUCH DAMAGE. 2737b1ce13SDoug Ambrisko */ 2837b1ce13SDoug Ambrisko 2937b1ce13SDoug Ambrisko #include <sys/param.h> 3037b1ce13SDoug Ambrisko #include <sys/systm.h> 31d72a0786SJohn Baldwin #include <sys/bus.h> 32d72a0786SJohn Baldwin #include <sys/condvar.h> 33d72a0786SJohn Baldwin #include <sys/conf.h> 34e2e050c8SConrad Meyer #include <sys/eventhandler.h> 3537b1ce13SDoug Ambrisko #include <sys/kernel.h> 36e2e050c8SConrad Meyer #include <sys/lock.h> 3737b1ce13SDoug Ambrisko #include <sys/malloc.h> 3837b1ce13SDoug Ambrisko #include <sys/module.h> 39e2e050c8SConrad Meyer #include <sys/mutex.h> 40d72a0786SJohn Baldwin #include <sys/poll.h> 411170c2feSWarner Losh #include <sys/reboot.h> 4237b1ce13SDoug Ambrisko #include <sys/rman.h> 43d72a0786SJohn Baldwin #include <sys/selinfo.h> 4437b1ce13SDoug Ambrisko #include <sys/sysctl.h> 45d72a0786SJohn Baldwin #include <sys/watchdog.h> 4637b1ce13SDoug Ambrisko 4737b1ce13SDoug Ambrisko #ifdef LOCAL_MODULE 4837b1ce13SDoug Ambrisko #include <ipmi.h> 4937b1ce13SDoug Ambrisko #include <ipmivars.h> 5037b1ce13SDoug Ambrisko #else 5137b1ce13SDoug Ambrisko #include <sys/ipmi.h> 5237b1ce13SDoug Ambrisko #include <dev/ipmi/ipmivars.h> 5337b1ce13SDoug Ambrisko #endif 5437b1ce13SDoug Ambrisko 55562894f0SBrooks Davis #ifdef IPMICTL_SEND_COMMAND_32 56562894f0SBrooks Davis #include <sys/abi_compat.h> 57562894f0SBrooks Davis #endif 58562894f0SBrooks Davis 59c869aa71SJohn Baldwin /* 60c869aa71SJohn Baldwin * Driver request structures are allocated on the stack via alloca() to 61c869aa71SJohn Baldwin * avoid calling malloc(), especially for the watchdog handler. 62c869aa71SJohn Baldwin * To avoid too much stack growth, a previously allocated structure can 63c869aa71SJohn Baldwin * be reused via IPMI_INIT_DRIVER_REQUEST(), but the caller should ensure 64c869aa71SJohn Baldwin * that there is adequate reply/request space in the original allocation. 65c869aa71SJohn Baldwin */ 66c869aa71SJohn Baldwin #define IPMI_INIT_DRIVER_REQUEST(req, addr, cmd, reqlen, replylen) \ 67c869aa71SJohn Baldwin bzero((req), sizeof(struct ipmi_request)); \ 68c869aa71SJohn Baldwin ipmi_init_request((req), NULL, 0, (addr), (cmd), (reqlen), (replylen)) 69c869aa71SJohn Baldwin 70c869aa71SJohn Baldwin #define IPMI_ALLOC_DRIVER_REQUEST(req, addr, cmd, reqlen, replylen) \ 71c869aa71SJohn Baldwin (req) = __builtin_alloca(sizeof(struct ipmi_request) + \ 72c869aa71SJohn Baldwin (reqlen) + (replylen)); \ 73c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST((req), (addr), (cmd), (reqlen), \ 74c869aa71SJohn Baldwin (replylen)) 75c869aa71SJohn Baldwin 7637b1ce13SDoug Ambrisko static d_ioctl_t ipmi_ioctl; 7737b1ce13SDoug Ambrisko static d_poll_t ipmi_poll; 7837b1ce13SDoug Ambrisko static d_open_t ipmi_open; 79943bebd2SJohn Baldwin static void ipmi_dtor(void *arg); 8037b1ce13SDoug Ambrisko 8137b1ce13SDoug Ambrisko int ipmi_attached = 0; 8237b1ce13SDoug Ambrisko 8337b1ce13SDoug Ambrisko static int on = 1; 8414d00450SWarner Losh static bool wd_in_shutdown = false; 8514d00450SWarner Losh static int wd_timer_actions = IPMI_SET_WD_ACTION_POWER_CYCLE; 869ee3ea71SPeter Wemm static int wd_shutdown_countdown = 0; /* sec */ 87c154763dSWarner Losh static int wd_startup_countdown = 0; /* sec */ 8814d00450SWarner Losh static int wd_pretimeout_countdown = 120; /* sec */ 8916f0063eSWarner Losh static int cycle_wait = 10; /* sec */ 90e3500c60SWojciech Macek static int wd_init_enable = 1; 9114d00450SWarner Losh 927029da5cSPawel Biernacki static SYSCTL_NODE(_hw, OID_AUTO, ipmi, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 936472ac3dSEd Schouten "IPMI driver parameters"); 9414d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RWTUN, 9537b1ce13SDoug Ambrisko &on, 0, ""); 96e3500c60SWojciech Macek SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_init_enable, CTLFLAG_RWTUN, 97e3500c60SWojciech Macek &wd_init_enable, 1, "Enable watchdog initialization"); 98016d1822SAlexander Motin SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_timer_actions, CTLFLAG_RWTUN, 9914d00450SWarner Losh &wd_timer_actions, 0, 10014d00450SWarner Losh "IPMI watchdog timer actions (including pre-timeout interrupt)"); 101016d1822SAlexander Motin SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_shutdown_countdown, CTLFLAG_RWTUN, 10214d00450SWarner Losh &wd_shutdown_countdown, 0, 10314d00450SWarner Losh "IPMI watchdog countdown for shutdown (seconds)"); 10414d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_startup_countdown, CTLFLAG_RDTUN, 10514d00450SWarner Losh &wd_startup_countdown, 0, 10614d00450SWarner Losh "IPMI watchdog countdown initialized during startup (seconds)"); 107016d1822SAlexander Motin SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_pretimeout_countdown, CTLFLAG_RWTUN, 10814d00450SWarner Losh &wd_pretimeout_countdown, 0, 10914d00450SWarner Losh "IPMI watchdog pre-timeout countdown (seconds)"); 1106d9d4b2dSEugene Grosbein SYSCTL_INT(_hw_ipmi, OID_AUTO, cycle_wait, CTLFLAG_RWTUN, 11116f0063eSWarner Losh &cycle_wait, 0, 11216f0063eSWarner Losh "IPMI power cycle on reboot delay time (seconds)"); 11337b1ce13SDoug Ambrisko 11437b1ce13SDoug Ambrisko static struct cdevsw ipmi_cdevsw = { 11537b1ce13SDoug Ambrisko .d_version = D_VERSION, 11637b1ce13SDoug Ambrisko .d_open = ipmi_open, 11737b1ce13SDoug Ambrisko .d_ioctl = ipmi_ioctl, 11837b1ce13SDoug Ambrisko .d_poll = ipmi_poll, 11937b1ce13SDoug Ambrisko .d_name = "ipmi", 12037b1ce13SDoug Ambrisko }; 12137b1ce13SDoug Ambrisko 122d745c852SEd Schouten static MALLOC_DEFINE(M_IPMI, "ipmi", "ipmi"); 12337b1ce13SDoug Ambrisko 12437b1ce13SDoug Ambrisko static int 125d72a0786SJohn Baldwin ipmi_open(struct cdev *cdev, int flags, int fmt, struct thread *td) 12637b1ce13SDoug Ambrisko { 127d72a0786SJohn Baldwin struct ipmi_device *dev; 12837b1ce13SDoug Ambrisko struct ipmi_softc *sc; 129943bebd2SJohn Baldwin int error; 13037b1ce13SDoug Ambrisko 13137b1ce13SDoug Ambrisko if (!on) 132d72a0786SJohn Baldwin return (ENOENT); 13337b1ce13SDoug Ambrisko 134943bebd2SJohn Baldwin /* Initialize the per file descriptor data. */ 135943bebd2SJohn Baldwin dev = malloc(sizeof(struct ipmi_device), M_IPMI, M_WAITOK | M_ZERO); 136943bebd2SJohn Baldwin error = devfs_set_cdevpriv(dev, ipmi_dtor); 137943bebd2SJohn Baldwin if (error) { 138943bebd2SJohn Baldwin free(dev, M_IPMI); 139943bebd2SJohn Baldwin return (error); 14037b1ce13SDoug Ambrisko } 141943bebd2SJohn Baldwin 142943bebd2SJohn Baldwin sc = cdev->si_drv1; 143943bebd2SJohn Baldwin TAILQ_INIT(&dev->ipmi_completed_requests); 144943bebd2SJohn Baldwin dev->ipmi_address = IPMI_BMC_SLAVE_ADDR; 145943bebd2SJohn Baldwin dev->ipmi_lun = IPMI_BMC_SMS_LUN; 146943bebd2SJohn Baldwin dev->ipmi_softc = sc; 147943bebd2SJohn Baldwin IPMI_LOCK(sc); 148943bebd2SJohn Baldwin sc->ipmi_opened++; 149d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 15037b1ce13SDoug Ambrisko 151d72a0786SJohn Baldwin return (0); 15237b1ce13SDoug Ambrisko } 15337b1ce13SDoug Ambrisko 15437b1ce13SDoug Ambrisko static int 155d72a0786SJohn Baldwin ipmi_poll(struct cdev *cdev, int poll_events, struct thread *td) 15637b1ce13SDoug Ambrisko { 157d72a0786SJohn Baldwin struct ipmi_device *dev; 15837b1ce13SDoug Ambrisko struct ipmi_softc *sc; 15937b1ce13SDoug Ambrisko int revents = 0; 16037b1ce13SDoug Ambrisko 161943bebd2SJohn Baldwin if (devfs_get_cdevpriv((void **)&dev)) 162943bebd2SJohn Baldwin return (0); 16337b1ce13SDoug Ambrisko 164943bebd2SJohn Baldwin sc = cdev->si_drv1; 165d72a0786SJohn Baldwin IPMI_LOCK(sc); 16637b1ce13SDoug Ambrisko if (poll_events & (POLLIN | POLLRDNORM)) { 167d72a0786SJohn Baldwin if (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) 16837b1ce13SDoug Ambrisko revents |= poll_events & (POLLIN | POLLRDNORM); 169d72a0786SJohn Baldwin if (dev->ipmi_requests == 0) 17037b1ce13SDoug Ambrisko revents |= POLLERR; 17137b1ce13SDoug Ambrisko } 17237b1ce13SDoug Ambrisko 17337b1ce13SDoug Ambrisko if (revents == 0) { 17437b1ce13SDoug Ambrisko if (poll_events & (POLLIN | POLLRDNORM)) 175d72a0786SJohn Baldwin selrecord(td, &dev->ipmi_select); 176d72a0786SJohn Baldwin } 177d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 178d72a0786SJohn Baldwin 179d72a0786SJohn Baldwin return (revents); 18037b1ce13SDoug Ambrisko } 18137b1ce13SDoug Ambrisko 182d72a0786SJohn Baldwin static void 183d72a0786SJohn Baldwin ipmi_purge_completed_requests(struct ipmi_device *dev) 184d72a0786SJohn Baldwin { 185d72a0786SJohn Baldwin struct ipmi_request *req; 186d72a0786SJohn Baldwin 187d72a0786SJohn Baldwin while (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) { 188d72a0786SJohn Baldwin req = TAILQ_FIRST(&dev->ipmi_completed_requests); 189d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, req, ir_link); 190d72a0786SJohn Baldwin dev->ipmi_requests--; 191d72a0786SJohn Baldwin ipmi_free_request(req); 192d72a0786SJohn Baldwin } 19337b1ce13SDoug Ambrisko } 19437b1ce13SDoug Ambrisko 195943bebd2SJohn Baldwin static void 196943bebd2SJohn Baldwin ipmi_dtor(void *arg) 19737b1ce13SDoug Ambrisko { 198d72a0786SJohn Baldwin struct ipmi_request *req, *nreq; 199d72a0786SJohn Baldwin struct ipmi_device *dev; 20037b1ce13SDoug Ambrisko struct ipmi_softc *sc; 20137b1ce13SDoug Ambrisko 202943bebd2SJohn Baldwin dev = arg; 203d72a0786SJohn Baldwin sc = dev->ipmi_softc; 20437b1ce13SDoug Ambrisko 205d72a0786SJohn Baldwin IPMI_LOCK(sc); 206d72a0786SJohn Baldwin if (dev->ipmi_requests) { 207d72a0786SJohn Baldwin /* Throw away any pending requests for this device. */ 208f0f3e3e9SChuck Silvers TAILQ_FOREACH_SAFE(req, &sc->ipmi_pending_requests_highpri, ir_link, 209f0f3e3e9SChuck Silvers nreq) { 210f0f3e3e9SChuck Silvers if (req->ir_owner == dev) { 211f0f3e3e9SChuck Silvers TAILQ_REMOVE(&sc->ipmi_pending_requests_highpri, req, 212f0f3e3e9SChuck Silvers ir_link); 213f0f3e3e9SChuck Silvers dev->ipmi_requests--; 214f0f3e3e9SChuck Silvers ipmi_free_request(req); 215f0f3e3e9SChuck Silvers } 216f0f3e3e9SChuck Silvers } 217d72a0786SJohn Baldwin TAILQ_FOREACH_SAFE(req, &sc->ipmi_pending_requests, ir_link, 218d72a0786SJohn Baldwin nreq) { 219d72a0786SJohn Baldwin if (req->ir_owner == dev) { 220d72a0786SJohn Baldwin TAILQ_REMOVE(&sc->ipmi_pending_requests, req, 221d72a0786SJohn Baldwin ir_link); 222d72a0786SJohn Baldwin dev->ipmi_requests--; 223d72a0786SJohn Baldwin ipmi_free_request(req); 224d72a0786SJohn Baldwin } 225d72a0786SJohn Baldwin } 22637b1ce13SDoug Ambrisko 227d72a0786SJohn Baldwin /* Throw away any pending completed requests for this device. */ 228d72a0786SJohn Baldwin ipmi_purge_completed_requests(dev); 229d72a0786SJohn Baldwin 230d72a0786SJohn Baldwin /* 231d72a0786SJohn Baldwin * If we still have outstanding requests, they must be stuck 232d72a0786SJohn Baldwin * in an interface driver, so wait for those to drain. 233d72a0786SJohn Baldwin */ 234d72a0786SJohn Baldwin dev->ipmi_closing = 1; 235d72a0786SJohn Baldwin while (dev->ipmi_requests > 0) { 236c869aa71SJohn Baldwin msleep(&dev->ipmi_requests, &sc->ipmi_requests_lock, 237c869aa71SJohn Baldwin PWAIT, "ipmidrain", 0); 238d72a0786SJohn Baldwin ipmi_purge_completed_requests(dev); 239d72a0786SJohn Baldwin } 240d72a0786SJohn Baldwin } 241943bebd2SJohn Baldwin sc->ipmi_opened--; 242d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 243d72a0786SJohn Baldwin 244d72a0786SJohn Baldwin /* Cleanup. */ 245d72a0786SJohn Baldwin free(dev, M_IPMI); 24637b1ce13SDoug Ambrisko } 24737b1ce13SDoug Ambrisko 24818db96dbSYuri static u_char 24937b1ce13SDoug Ambrisko ipmi_ipmb_checksum(u_char *data, int len) 25037b1ce13SDoug Ambrisko { 25137b1ce13SDoug Ambrisko u_char sum = 0; 25237b1ce13SDoug Ambrisko 25318db96dbSYuri for (; len; len--) 25437b1ce13SDoug Ambrisko sum += *data++; 255d72a0786SJohn Baldwin return (-sum); 25637b1ce13SDoug Ambrisko } 25737b1ce13SDoug Ambrisko 25837b1ce13SDoug Ambrisko static int 259d72a0786SJohn Baldwin ipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, 26037b1ce13SDoug Ambrisko int flags, struct thread *td) 26137b1ce13SDoug Ambrisko { 26237b1ce13SDoug Ambrisko struct ipmi_softc *sc; 263d72a0786SJohn Baldwin struct ipmi_device *dev; 264d72a0786SJohn Baldwin struct ipmi_request *kreq; 26537b1ce13SDoug Ambrisko struct ipmi_req *req = (struct ipmi_req *)data; 26637b1ce13SDoug Ambrisko struct ipmi_recv *recv = (struct ipmi_recv *)data; 26737b1ce13SDoug Ambrisko struct ipmi_addr addr; 268d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 269d72a0786SJohn Baldwin struct ipmi_req32 *req32 = (struct ipmi_req32 *)data; 270d72a0786SJohn Baldwin struct ipmi_recv32 *recv32 = (struct ipmi_recv32 *)data; 271d72a0786SJohn Baldwin union { 272d72a0786SJohn Baldwin struct ipmi_req req; 273d72a0786SJohn Baldwin struct ipmi_recv recv; 274d72a0786SJohn Baldwin } thunk32; 275d72a0786SJohn Baldwin #endif 27637b1ce13SDoug Ambrisko int error, len; 27737b1ce13SDoug Ambrisko 278943bebd2SJohn Baldwin error = devfs_get_cdevpriv((void **)&dev); 279943bebd2SJohn Baldwin if (error) 280943bebd2SJohn Baldwin return (error); 281943bebd2SJohn Baldwin 282943bebd2SJohn Baldwin sc = cdev->si_drv1; 283d72a0786SJohn Baldwin 284d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 285d72a0786SJohn Baldwin /* Convert 32-bit structures to native. */ 286d72a0786SJohn Baldwin switch (cmd) { 287d72a0786SJohn Baldwin case IPMICTL_SEND_COMMAND_32: 288d72a0786SJohn Baldwin req = &thunk32.req; 289d72a0786SJohn Baldwin req->addr = PTRIN(req32->addr); 290d72a0786SJohn Baldwin req->addr_len = req32->addr_len; 291d72a0786SJohn Baldwin req->msgid = req32->msgid; 292d72a0786SJohn Baldwin req->msg.netfn = req32->msg.netfn; 293d72a0786SJohn Baldwin req->msg.cmd = req32->msg.cmd; 294d72a0786SJohn Baldwin req->msg.data_len = req32->msg.data_len; 295d72a0786SJohn Baldwin req->msg.data = PTRIN(req32->msg.data); 296d72a0786SJohn Baldwin break; 297d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 298d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 299d72a0786SJohn Baldwin recv = &thunk32.recv; 300d72a0786SJohn Baldwin recv->addr = PTRIN(recv32->addr); 301d72a0786SJohn Baldwin recv->addr_len = recv32->addr_len; 302d72a0786SJohn Baldwin recv->msg.data_len = recv32->msg.data_len; 303d72a0786SJohn Baldwin recv->msg.data = PTRIN(recv32->msg.data); 304d72a0786SJohn Baldwin break; 305d72a0786SJohn Baldwin } 306d72a0786SJohn Baldwin #endif 30737b1ce13SDoug Ambrisko 30837b1ce13SDoug Ambrisko switch (cmd) { 309d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 310d72a0786SJohn Baldwin case IPMICTL_SEND_COMMAND_32: 311d72a0786SJohn Baldwin #endif 31237b1ce13SDoug Ambrisko case IPMICTL_SEND_COMMAND: 31337b1ce13SDoug Ambrisko error = copyin(req->addr, &addr, sizeof(addr)); 314d72a0786SJohn Baldwin if (error) 315d72a0786SJohn Baldwin return (error); 316d72a0786SJohn Baldwin 31718db96dbSYuri if (addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { 318177f8b32SYuri struct ipmi_system_interface_addr *saddr = 319177f8b32SYuri (struct ipmi_system_interface_addr *)&addr; 320177f8b32SYuri 321d72a0786SJohn Baldwin kreq = ipmi_alloc_request(dev, req->msgid, 322177f8b32SYuri IPMI_ADDR(req->msg.netfn, saddr->lun & 0x3), 323177f8b32SYuri req->msg.cmd, req->msg.data_len, IPMI_MAX_RX); 324d72a0786SJohn Baldwin error = copyin(req->msg.data, kreq->ir_request, 32537b1ce13SDoug Ambrisko req->msg.data_len); 326d72a0786SJohn Baldwin if (error) { 327d72a0786SJohn Baldwin ipmi_free_request(kreq); 328d72a0786SJohn Baldwin return (error); 32937b1ce13SDoug Ambrisko } 330d72a0786SJohn Baldwin IPMI_LOCK(sc); 331d72a0786SJohn Baldwin dev->ipmi_requests++; 332d72a0786SJohn Baldwin error = sc->ipmi_enqueue_request(sc, kreq); 333d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 334d72a0786SJohn Baldwin if (error) 335d72a0786SJohn Baldwin return (error); 336d72a0786SJohn Baldwin break; 33718db96dbSYuri } 33818db96dbSYuri 33918db96dbSYuri /* Special processing for IPMB commands */ 34018db96dbSYuri struct ipmi_ipmb_addr *iaddr = (struct ipmi_ipmb_addr *)&addr; 34118db96dbSYuri 34218db96dbSYuri IPMI_ALLOC_DRIVER_REQUEST(kreq, IPMI_ADDR(IPMI_APP_REQUEST, 0), 34318db96dbSYuri IPMI_SEND_MSG, req->msg.data_len + 8, IPMI_MAX_RX); 34418db96dbSYuri /* Construct the SEND MSG header */ 34518db96dbSYuri kreq->ir_request[0] = iaddr->channel; 34618db96dbSYuri kreq->ir_request[1] = iaddr->slave_addr; 34718db96dbSYuri kreq->ir_request[2] = IPMI_ADDR(req->msg.netfn, iaddr->lun); 34818db96dbSYuri kreq->ir_request[3] = 34918db96dbSYuri ipmi_ipmb_checksum(&kreq->ir_request[1], 2); 35018db96dbSYuri kreq->ir_request[4] = dev->ipmi_address; 35118db96dbSYuri kreq->ir_request[5] = IPMI_ADDR(0, dev->ipmi_lun); 35218db96dbSYuri kreq->ir_request[6] = req->msg.cmd; 35318db96dbSYuri /* Copy the message data */ 35418db96dbSYuri if (req->msg.data_len > 0) { 35518db96dbSYuri error = copyin(req->msg.data, &kreq->ir_request[7], 35618db96dbSYuri req->msg.data_len); 35718db96dbSYuri if (error != 0) 35818db96dbSYuri return (error); 35918db96dbSYuri } 36018db96dbSYuri kreq->ir_request[req->msg.data_len + 7] = 36118db96dbSYuri ipmi_ipmb_checksum(&kreq->ir_request[4], 36218db96dbSYuri req->msg.data_len + 3); 36318db96dbSYuri error = ipmi_submit_driver_request(sc, kreq, MAX_TIMEOUT); 36418db96dbSYuri if (error != 0) 36518db96dbSYuri return (error); 36618db96dbSYuri 36718db96dbSYuri kreq = ipmi_alloc_request(dev, req->msgid, 36818db96dbSYuri IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG, 36918db96dbSYuri 0, IPMI_MAX_RX); 37018db96dbSYuri kreq->ir_ipmb = true; 37118db96dbSYuri kreq->ir_ipmb_addr = IPMI_ADDR(req->msg.netfn, 0); 37218db96dbSYuri kreq->ir_ipmb_command = req->msg.cmd; 37318db96dbSYuri IPMI_LOCK(sc); 37418db96dbSYuri dev->ipmi_requests++; 37518db96dbSYuri error = sc->ipmi_enqueue_request(sc, kreq); 37618db96dbSYuri IPMI_UNLOCK(sc); 37718db96dbSYuri if (error != 0) 37818db96dbSYuri return (error); 37918db96dbSYuri break; 380d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 381d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 382d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 383d72a0786SJohn Baldwin #endif 38437b1ce13SDoug Ambrisko case IPMICTL_RECEIVE_MSG_TRUNC: 38537b1ce13SDoug Ambrisko case IPMICTL_RECEIVE_MSG: 38637b1ce13SDoug Ambrisko error = copyin(recv->addr, &addr, sizeof(addr)); 387d72a0786SJohn Baldwin if (error) 388d72a0786SJohn Baldwin return (error); 389d72a0786SJohn Baldwin 390d72a0786SJohn Baldwin IPMI_LOCK(sc); 391d72a0786SJohn Baldwin kreq = TAILQ_FIRST(&dev->ipmi_completed_requests); 392d72a0786SJohn Baldwin if (kreq == NULL) { 393d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 394d72a0786SJohn Baldwin return (EAGAIN); 39537b1ce13SDoug Ambrisko } 39618db96dbSYuri if (kreq->ir_error != 0) { 397c4995b69SPhilip Paeps error = kreq->ir_error; 398d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, 399d72a0786SJohn Baldwin ir_link); 400d72a0786SJohn Baldwin dev->ipmi_requests--; 401d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 402d72a0786SJohn Baldwin ipmi_free_request(kreq); 403c4995b69SPhilip Paeps return (error); 404d72a0786SJohn Baldwin } 40518db96dbSYuri 40618db96dbSYuri recv->recv_type = IPMI_RESPONSE_RECV_TYPE; 40718db96dbSYuri recv->msgid = kreq->ir_msgid; 40818db96dbSYuri if (kreq->ir_ipmb) { 40918db96dbSYuri addr.channel = IPMI_IPMB_CHANNEL; 41018db96dbSYuri recv->msg.netfn = 41118db96dbSYuri IPMI_REPLY_ADDR(kreq->ir_ipmb_addr) >> 2; 41218db96dbSYuri recv->msg.cmd = kreq->ir_ipmb_command; 41318db96dbSYuri /* Get the compcode of response */ 41418db96dbSYuri kreq->ir_compcode = kreq->ir_reply[6]; 41518db96dbSYuri /* Move the reply head past response header */ 41618db96dbSYuri kreq->ir_reply += 7; 41718db96dbSYuri len = kreq->ir_replylen - 7; 41818db96dbSYuri } else { 41918db96dbSYuri addr.channel = IPMI_BMC_CHANNEL; 42018db96dbSYuri recv->msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2; 42118db96dbSYuri recv->msg.cmd = kreq->ir_command; 422d72a0786SJohn Baldwin len = kreq->ir_replylen + 1; 42318db96dbSYuri } 42418db96dbSYuri 425d72a0786SJohn Baldwin if (recv->msg.data_len < len && 426d72a0786SJohn Baldwin (cmd == IPMICTL_RECEIVE_MSG 427d72a0786SJohn Baldwin #ifdef IPMICTL_RECEIVE_MSG_32 428c12dbd1dSDavid E. O'Brien || cmd == IPMICTL_RECEIVE_MSG_32 429d72a0786SJohn Baldwin #endif 430d72a0786SJohn Baldwin )) { 431d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 432d72a0786SJohn Baldwin return (EMSGSIZE); 433d72a0786SJohn Baldwin } 434d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link); 435d72a0786SJohn Baldwin dev->ipmi_requests--; 436d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 43737b1ce13SDoug Ambrisko len = min(recv->msg.data_len, len); 43837b1ce13SDoug Ambrisko recv->msg.data_len = len; 43937b1ce13SDoug Ambrisko error = copyout(&addr, recv->addr,sizeof(addr)); 44037b1ce13SDoug Ambrisko if (error == 0) 441d72a0786SJohn Baldwin error = copyout(&kreq->ir_compcode, recv->msg.data, 1); 442d72a0786SJohn Baldwin if (error == 0) 443d72a0786SJohn Baldwin error = copyout(kreq->ir_reply, recv->msg.data + 1, 444d72a0786SJohn Baldwin len - 1); 445d72a0786SJohn Baldwin ipmi_free_request(kreq); 446d72a0786SJohn Baldwin if (error) 447d72a0786SJohn Baldwin return (error); 448d72a0786SJohn Baldwin break; 44937b1ce13SDoug Ambrisko case IPMICTL_SET_MY_ADDRESS_CMD: 450d72a0786SJohn Baldwin IPMI_LOCK(sc); 451d72a0786SJohn Baldwin dev->ipmi_address = *(int*)data; 452d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 453d72a0786SJohn Baldwin break; 45437b1ce13SDoug Ambrisko case IPMICTL_GET_MY_ADDRESS_CMD: 455d72a0786SJohn Baldwin IPMI_LOCK(sc); 456d72a0786SJohn Baldwin *(int*)data = dev->ipmi_address; 457d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 458d72a0786SJohn Baldwin break; 45937b1ce13SDoug Ambrisko case IPMICTL_SET_MY_LUN_CMD: 460d72a0786SJohn Baldwin IPMI_LOCK(sc); 461d72a0786SJohn Baldwin dev->ipmi_lun = *(int*)data & 0x3; 462d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 463d72a0786SJohn Baldwin break; 46437b1ce13SDoug Ambrisko case IPMICTL_GET_MY_LUN_CMD: 465d72a0786SJohn Baldwin IPMI_LOCK(sc); 466d72a0786SJohn Baldwin *(int*)data = dev->ipmi_lun; 467d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 468d72a0786SJohn Baldwin break; 46937b1ce13SDoug Ambrisko case IPMICTL_SET_GETS_EVENTS_CMD: 47037b1ce13SDoug Ambrisko /* 47137b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, 47237b1ce13SDoug Ambrisko "IPMICTL_SET_GETS_EVENTS_CMD NA\n"); 47337b1ce13SDoug Ambrisko */ 474d72a0786SJohn Baldwin break; 47537b1ce13SDoug Ambrisko case IPMICTL_REGISTER_FOR_CMD: 47637b1ce13SDoug Ambrisko case IPMICTL_UNREGISTER_FOR_CMD: 477d72a0786SJohn Baldwin return (EOPNOTSUPP); 478d72a0786SJohn Baldwin default: 47937b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "Unknown IOCTL %lX\n", cmd); 480d72a0786SJohn Baldwin return (ENOIOCTL); 48137b1ce13SDoug Ambrisko } 48237b1ce13SDoug Ambrisko 483d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 484d72a0786SJohn Baldwin /* Update changed fields in 32-bit structures. */ 485d72a0786SJohn Baldwin switch (cmd) { 486d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 487d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 488d72a0786SJohn Baldwin recv32->recv_type = recv->recv_type; 489d72a0786SJohn Baldwin recv32->msgid = recv->msgid; 490d72a0786SJohn Baldwin recv32->msg.netfn = recv->msg.netfn; 491d72a0786SJohn Baldwin recv32->msg.cmd = recv->msg.cmd; 492d72a0786SJohn Baldwin recv32->msg.data_len = recv->msg.data_len; 49337b1ce13SDoug Ambrisko break; 49437b1ce13SDoug Ambrisko } 495d72a0786SJohn Baldwin #endif 496d72a0786SJohn Baldwin return (0); 49737b1ce13SDoug Ambrisko } 49837b1ce13SDoug Ambrisko 499d72a0786SJohn Baldwin /* 500d72a0786SJohn Baldwin * Request management. 501d72a0786SJohn Baldwin */ 50237b1ce13SDoug Ambrisko 50318db96dbSYuri __inline void 504c869aa71SJohn Baldwin ipmi_init_request(struct ipmi_request *req, struct ipmi_device *dev, long msgid, 505c869aa71SJohn Baldwin uint8_t addr, uint8_t command, size_t requestlen, size_t replylen) 506d72a0786SJohn Baldwin { 507d72a0786SJohn Baldwin 508d72a0786SJohn Baldwin req->ir_owner = dev; 509d72a0786SJohn Baldwin req->ir_msgid = msgid; 510d72a0786SJohn Baldwin req->ir_addr = addr; 511d72a0786SJohn Baldwin req->ir_command = command; 512d72a0786SJohn Baldwin if (requestlen) { 513d72a0786SJohn Baldwin req->ir_request = (char *)&req[1]; 514d72a0786SJohn Baldwin req->ir_requestlen = requestlen; 515d72a0786SJohn Baldwin } 516d72a0786SJohn Baldwin if (replylen) { 517d72a0786SJohn Baldwin req->ir_reply = (char *)&req[1] + requestlen; 518d72a0786SJohn Baldwin req->ir_replybuflen = replylen; 519d72a0786SJohn Baldwin } 520c869aa71SJohn Baldwin } 521c869aa71SJohn Baldwin 522c869aa71SJohn Baldwin /* Allocate a new request with request and reply buffers. */ 523c869aa71SJohn Baldwin struct ipmi_request * 524c869aa71SJohn Baldwin ipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr, 525c869aa71SJohn Baldwin uint8_t command, size_t requestlen, size_t replylen) 526c869aa71SJohn Baldwin { 527c869aa71SJohn Baldwin struct ipmi_request *req; 528c869aa71SJohn Baldwin 529c869aa71SJohn Baldwin req = malloc(sizeof(struct ipmi_request) + requestlen + replylen, 530c869aa71SJohn Baldwin M_IPMI, M_WAITOK | M_ZERO); 531c869aa71SJohn Baldwin ipmi_init_request(req, dev, msgid, addr, command, requestlen, replylen); 532d72a0786SJohn Baldwin return (req); 53337b1ce13SDoug Ambrisko } 53437b1ce13SDoug Ambrisko 535d72a0786SJohn Baldwin /* Free a request no longer in use. */ 536d72a0786SJohn Baldwin void 537d72a0786SJohn Baldwin ipmi_free_request(struct ipmi_request *req) 538d72a0786SJohn Baldwin { 53937b1ce13SDoug Ambrisko 540d72a0786SJohn Baldwin free(req, M_IPMI); 54137b1ce13SDoug Ambrisko } 54237b1ce13SDoug Ambrisko 543d72a0786SJohn Baldwin /* Store a processed request on the appropriate completion queue. */ 544d72a0786SJohn Baldwin void 545d72a0786SJohn Baldwin ipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req) 546d72a0786SJohn Baldwin { 547d72a0786SJohn Baldwin struct ipmi_device *dev; 54837b1ce13SDoug Ambrisko 549d72a0786SJohn Baldwin IPMI_LOCK_ASSERT(sc); 550d72a0786SJohn Baldwin 551d72a0786SJohn Baldwin /* 552d72a0786SJohn Baldwin * Anonymous requests (from inside the driver) always have a 553d72a0786SJohn Baldwin * waiter that we awaken. 554d72a0786SJohn Baldwin */ 555d72a0786SJohn Baldwin if (req->ir_owner == NULL) 556d72a0786SJohn Baldwin wakeup(req); 557d72a0786SJohn Baldwin else { 558d72a0786SJohn Baldwin dev = req->ir_owner; 559d72a0786SJohn Baldwin TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link); 560d72a0786SJohn Baldwin selwakeup(&dev->ipmi_select); 561d72a0786SJohn Baldwin if (dev->ipmi_closing) 562d72a0786SJohn Baldwin wakeup(&dev->ipmi_requests); 563d72a0786SJohn Baldwin } 56437b1ce13SDoug Ambrisko } 56537b1ce13SDoug Ambrisko 566c869aa71SJohn Baldwin /* Perform an internal driver request. */ 56737b1ce13SDoug Ambrisko int 568d72a0786SJohn Baldwin ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, 569d72a0786SJohn Baldwin int timo) 570d72a0786SJohn Baldwin { 57137b1ce13SDoug Ambrisko 572c869aa71SJohn Baldwin return (sc->ipmi_driver_request(sc, req, timo)); 57337b1ce13SDoug Ambrisko } 57437b1ce13SDoug Ambrisko 575d72a0786SJohn Baldwin /* 576d72a0786SJohn Baldwin * Helper routine for polled system interfaces that use 577d72a0786SJohn Baldwin * ipmi_polled_enqueue_request() to queue requests. This request 578d72a0786SJohn Baldwin * waits until there is a pending request and then returns the first 579d72a0786SJohn Baldwin * request. If the driver is shutting down, it returns NULL. 580d72a0786SJohn Baldwin */ 581d72a0786SJohn Baldwin struct ipmi_request * 582d72a0786SJohn Baldwin ipmi_dequeue_request(struct ipmi_softc *sc) 583d72a0786SJohn Baldwin { 584d72a0786SJohn Baldwin struct ipmi_request *req; 58537b1ce13SDoug Ambrisko 586d72a0786SJohn Baldwin IPMI_LOCK_ASSERT(sc); 58737b1ce13SDoug Ambrisko 588f0f3e3e9SChuck Silvers while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests) && 589f0f3e3e9SChuck Silvers TAILQ_EMPTY(&sc->ipmi_pending_requests_highpri)) 590c869aa71SJohn Baldwin cv_wait(&sc->ipmi_request_added, &sc->ipmi_requests_lock); 591d72a0786SJohn Baldwin if (sc->ipmi_detaching) 592d72a0786SJohn Baldwin return (NULL); 59337b1ce13SDoug Ambrisko 594f0f3e3e9SChuck Silvers req = TAILQ_FIRST(&sc->ipmi_pending_requests_highpri); 595f0f3e3e9SChuck Silvers if (req != NULL) 596f0f3e3e9SChuck Silvers TAILQ_REMOVE(&sc->ipmi_pending_requests_highpri, req, ir_link); 597f0f3e3e9SChuck Silvers else { 598d72a0786SJohn Baldwin req = TAILQ_FIRST(&sc->ipmi_pending_requests); 599d72a0786SJohn Baldwin TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); 600f0f3e3e9SChuck Silvers } 601d72a0786SJohn Baldwin return (req); 60237b1ce13SDoug Ambrisko } 60337b1ce13SDoug Ambrisko 604d72a0786SJohn Baldwin /* Default implementation of ipmi_enqueue_request() for polled interfaces. */ 605d72a0786SJohn Baldwin int 606d72a0786SJohn Baldwin ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req) 607d72a0786SJohn Baldwin { 60837b1ce13SDoug Ambrisko 6095283d39bSJohn Baldwin IPMI_LOCK_ASSERT(sc); 6105283d39bSJohn Baldwin 611d72a0786SJohn Baldwin TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link); 612d72a0786SJohn Baldwin cv_signal(&sc->ipmi_request_added); 613d72a0786SJohn Baldwin return (0); 61437b1ce13SDoug Ambrisko } 61537b1ce13SDoug Ambrisko 616f0f3e3e9SChuck Silvers int 617f0f3e3e9SChuck Silvers ipmi_polled_enqueue_request_highpri(struct ipmi_softc *sc, struct ipmi_request *req) 618f0f3e3e9SChuck Silvers { 619f0f3e3e9SChuck Silvers 620f0f3e3e9SChuck Silvers IPMI_LOCK_ASSERT(sc); 621f0f3e3e9SChuck Silvers 622f0f3e3e9SChuck Silvers TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests_highpri, req, ir_link); 623f0f3e3e9SChuck Silvers cv_signal(&sc->ipmi_request_added); 624f0f3e3e9SChuck Silvers return (0); 625f0f3e3e9SChuck Silvers } 626f0f3e3e9SChuck Silvers 62737b1ce13SDoug Ambrisko /* 62837b1ce13SDoug Ambrisko * Watchdog event handler. 62937b1ce13SDoug Ambrisko */ 63037b1ce13SDoug Ambrisko 631a46a1e76SRuslan Ermilov static int 632ea2ef993SAlexander Motin ipmi_reset_watchdog(struct ipmi_softc *sc) 633ea2ef993SAlexander Motin { 634ea2ef993SAlexander Motin struct ipmi_request *req; 635ea2ef993SAlexander Motin int error; 636ea2ef993SAlexander Motin 637ea2ef993SAlexander Motin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 638ea2ef993SAlexander Motin IPMI_RESET_WDOG, 0, 0); 639ea2ef993SAlexander Motin error = ipmi_submit_driver_request(sc, req, 0); 6409d3b47abSAlexander Motin if (error) { 641ea2ef993SAlexander Motin device_printf(sc->ipmi_dev, "Failed to reset watchdog\n"); 6429d3b47abSAlexander Motin } else if (req->ir_compcode == 0x80) { 6439d3b47abSAlexander Motin error = ENOENT; 6449d3b47abSAlexander Motin } else if (req->ir_compcode != 0) { 6459d3b47abSAlexander Motin device_printf(sc->ipmi_dev, "Watchdog reset returned 0x%x\n", 6469d3b47abSAlexander Motin req->ir_compcode); 6479d3b47abSAlexander Motin error = EINVAL; 6489d3b47abSAlexander Motin } 649ea2ef993SAlexander Motin return (error); 650ea2ef993SAlexander Motin } 651ea2ef993SAlexander Motin 652ea2ef993SAlexander Motin static int 653a46a1e76SRuslan Ermilov ipmi_set_watchdog(struct ipmi_softc *sc, unsigned int sec) 654d72a0786SJohn Baldwin { 655d72a0786SJohn Baldwin struct ipmi_request *req; 656d72a0786SJohn Baldwin int error; 65737b1ce13SDoug Ambrisko 658a46a1e76SRuslan Ermilov if (sec > 0xffff / 10) 659a46a1e76SRuslan Ermilov return (EINVAL); 660a46a1e76SRuslan Ermilov 661c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 662d72a0786SJohn Baldwin IPMI_SET_WDOG, 6, 0); 66337b1ce13SDoug Ambrisko if (sec) { 664d72a0786SJohn Baldwin req->ir_request[0] = IPMI_SET_WD_TIMER_DONT_STOP 66537b1ce13SDoug Ambrisko | IPMI_SET_WD_TIMER_SMS_OS; 66614d00450SWarner Losh req->ir_request[1] = (wd_timer_actions & 0xff); 6676c2d4404SAlexander Motin req->ir_request[2] = min(0xff, 6686c2d4404SAlexander Motin min(wd_pretimeout_countdown, (sec + 2) / 4)); 669d72a0786SJohn Baldwin req->ir_request[3] = 0; /* Timer use */ 670d72a0786SJohn Baldwin req->ir_request[4] = (sec * 10) & 0xff; 671a46a1e76SRuslan Ermilov req->ir_request[5] = (sec * 10) >> 8; 67237b1ce13SDoug Ambrisko } else { 673d72a0786SJohn Baldwin req->ir_request[0] = IPMI_SET_WD_TIMER_SMS_OS; 674d72a0786SJohn Baldwin req->ir_request[1] = 0; 675d72a0786SJohn Baldwin req->ir_request[2] = 0; 676d72a0786SJohn Baldwin req->ir_request[3] = 0; /* Timer use */ 677d72a0786SJohn Baldwin req->ir_request[4] = 0; 678d72a0786SJohn Baldwin req->ir_request[5] = 0; 67937b1ce13SDoug Ambrisko } 680d72a0786SJohn Baldwin error = ipmi_submit_driver_request(sc, req, 0); 6819d3b47abSAlexander Motin if (error) { 682d72a0786SJohn Baldwin device_printf(sc->ipmi_dev, "Failed to set watchdog\n"); 6839d3b47abSAlexander Motin } else if (req->ir_compcode != 0) { 6849d3b47abSAlexander Motin device_printf(sc->ipmi_dev, "Watchdog set returned 0x%x\n", 6859d3b47abSAlexander Motin req->ir_compcode); 6869d3b47abSAlexander Motin error = EINVAL; 6879d3b47abSAlexander Motin } 688a46a1e76SRuslan Ermilov return (error); 68937b1ce13SDoug Ambrisko } 69037b1ce13SDoug Ambrisko 69137b1ce13SDoug Ambrisko static void 69237b1ce13SDoug Ambrisko ipmi_wd_event(void *arg, unsigned int cmd, int *error) 69337b1ce13SDoug Ambrisko { 69437b1ce13SDoug Ambrisko struct ipmi_softc *sc = arg; 69537b1ce13SDoug Ambrisko unsigned int timeout; 696a46a1e76SRuslan Ermilov int e; 69737b1ce13SDoug Ambrisko 69814d00450SWarner Losh /* Ignore requests while disabled. */ 69914d00450SWarner Losh if (!on) 700a9b3c1bfSGleb Smirnoff return; 701a9b3c1bfSGleb Smirnoff 70214d00450SWarner Losh /* 70314d00450SWarner Losh * To prevent infinite hangs, we don't let anyone pat or change 70414d00450SWarner Losh * the watchdog when we're shutting down. (See ipmi_shutdown_event().) 70514d00450SWarner Losh * However, we do want to keep patting the watchdog while we are doing 70614d00450SWarner Losh * a coredump. 70714d00450SWarner Losh */ 70814d00450SWarner Losh if (wd_in_shutdown) { 70914d00450SWarner Losh if (dumping && sc->ipmi_watchdog_active) 71014d00450SWarner Losh ipmi_reset_watchdog(sc); 71114d00450SWarner Losh return; 71214d00450SWarner Losh } 71314d00450SWarner Losh 71437b1ce13SDoug Ambrisko cmd &= WD_INTERVAL; 7159079fff5SNick Hibma if (cmd > 0 && cmd <= 63) { 716a46a1e76SRuslan Ermilov timeout = ((uint64_t)1 << cmd) / 1000000000; 717a46a1e76SRuslan Ermilov if (timeout == 0) 718a46a1e76SRuslan Ermilov timeout = 1; 71914d00450SWarner Losh if (timeout != sc->ipmi_watchdog_active || 72014d00450SWarner Losh wd_timer_actions != sc->ipmi_watchdog_actions || 72114d00450SWarner Losh wd_pretimeout_countdown != sc->ipmi_watchdog_pretimeout) { 722a46a1e76SRuslan Ermilov e = ipmi_set_watchdog(sc, timeout); 7231710852eSJohn Baldwin if (e == 0) { 724ea2ef993SAlexander Motin sc->ipmi_watchdog_active = timeout; 72514d00450SWarner Losh sc->ipmi_watchdog_actions = wd_timer_actions; 72614d00450SWarner Losh sc->ipmi_watchdog_pretimeout = wd_pretimeout_countdown; 727ea2ef993SAlexander Motin } else { 728a46a1e76SRuslan Ermilov (void)ipmi_set_watchdog(sc, 0); 729ea2ef993SAlexander Motin sc->ipmi_watchdog_active = 0; 73014d00450SWarner Losh sc->ipmi_watchdog_actions = 0; 73114d00450SWarner Losh sc->ipmi_watchdog_pretimeout = 0; 732ea2ef993SAlexander Motin } 733ea2ef993SAlexander Motin } 734ea2ef993SAlexander Motin if (sc->ipmi_watchdog_active != 0) { 735ea2ef993SAlexander Motin e = ipmi_reset_watchdog(sc); 736ea2ef993SAlexander Motin if (e == 0) { 737ea2ef993SAlexander Motin *error = 0; 738ea2ef993SAlexander Motin } else { 739ea2ef993SAlexander Motin (void)ipmi_set_watchdog(sc, 0); 740ea2ef993SAlexander Motin sc->ipmi_watchdog_active = 0; 74114d00450SWarner Losh sc->ipmi_watchdog_actions = 0; 74214d00450SWarner Losh sc->ipmi_watchdog_pretimeout = 0; 743ea2ef993SAlexander Motin } 744ea2ef993SAlexander Motin } 7451710852eSJohn Baldwin } else if (atomic_readandclear_int(&sc->ipmi_watchdog_active) != 0) { 74614d00450SWarner Losh sc->ipmi_watchdog_actions = 0; 74714d00450SWarner Losh sc->ipmi_watchdog_pretimeout = 0; 74814d00450SWarner Losh 749a46a1e76SRuslan Ermilov e = ipmi_set_watchdog(sc, 0); 750a46a1e76SRuslan Ermilov if (e != 0 && cmd == 0) 751a46a1e76SRuslan Ermilov *error = EOPNOTSUPP; 7529079fff5SNick Hibma } 75337b1ce13SDoug Ambrisko } 75437b1ce13SDoug Ambrisko 755d72a0786SJohn Baldwin static void 756*90dc7889SAndriy Gapon ipmi_shutdown_event(void *arg, int howto) 75714d00450SWarner Losh { 75814d00450SWarner Losh struct ipmi_softc *sc = arg; 75914d00450SWarner Losh 76014d00450SWarner Losh /* Ignore event if disabled. */ 76114d00450SWarner Losh if (!on) 76214d00450SWarner Losh return; 76314d00450SWarner Losh 76414d00450SWarner Losh /* 76514d00450SWarner Losh * Positive wd_shutdown_countdown value will re-arm watchdog; 76614d00450SWarner Losh * Zero value in wd_shutdown_countdown will disable watchdog; 76714d00450SWarner Losh * Negative value in wd_shutdown_countdown will keep existing state; 76814d00450SWarner Losh * 76914d00450SWarner Losh * Revert to using a power cycle to ensure that the watchdog will 77014d00450SWarner Losh * do something useful here. Having the watchdog send an NMI 77114d00450SWarner Losh * instead is useless during shutdown, and might be ignored if an 77214d00450SWarner Losh * NMI already triggered. 77314d00450SWarner Losh */ 77414d00450SWarner Losh 77514d00450SWarner Losh wd_in_shutdown = true; 77614d00450SWarner Losh if (wd_shutdown_countdown == 0) { 77714d00450SWarner Losh /* disable watchdog */ 77814d00450SWarner Losh ipmi_set_watchdog(sc, 0); 77914d00450SWarner Losh sc->ipmi_watchdog_active = 0; 78014d00450SWarner Losh } else if (wd_shutdown_countdown > 0) { 78114d00450SWarner Losh /* set desired action and time, and, reset watchdog */ 78214d00450SWarner Losh wd_timer_actions = IPMI_SET_WD_ACTION_POWER_CYCLE; 78314d00450SWarner Losh ipmi_set_watchdog(sc, wd_shutdown_countdown); 78414d00450SWarner Losh sc->ipmi_watchdog_active = wd_shutdown_countdown; 78514d00450SWarner Losh ipmi_reset_watchdog(sc); 78614d00450SWarner Losh } 78714d00450SWarner Losh } 78814d00450SWarner Losh 78914d00450SWarner Losh static void 7901170c2feSWarner Losh ipmi_power_cycle(void *arg, int howto) 7911170c2feSWarner Losh { 7921170c2feSWarner Losh struct ipmi_softc *sc = arg; 7931170c2feSWarner Losh struct ipmi_request *req; 7941170c2feSWarner Losh 7951170c2feSWarner Losh /* 7961170c2feSWarner Losh * Ignore everything except power cycling requests 7971170c2feSWarner Losh */ 7981170c2feSWarner Losh if ((howto & RB_POWERCYCLE) == 0) 7991170c2feSWarner Losh return; 8001170c2feSWarner Losh 8011170c2feSWarner Losh device_printf(sc->ipmi_dev, "Power cycling using IPMI\n"); 8021170c2feSWarner Losh 8031170c2feSWarner Losh /* 8041170c2feSWarner Losh * Send a CHASSIS_CONTROL command to the CHASSIS device, subcommand 2 8051170c2feSWarner Losh * as described in IPMI v2.0 spec section 28.3. 8061170c2feSWarner Losh */ 8071170c2feSWarner Losh IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_CHASSIS_REQUEST, 0), 8081170c2feSWarner Losh IPMI_CHASSIS_CONTROL, 1, 0); 8091170c2feSWarner Losh req->ir_request[0] = IPMI_CC_POWER_CYCLE; 8101170c2feSWarner Losh 8111170c2feSWarner Losh ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); 8121170c2feSWarner Losh 8131170c2feSWarner Losh if (req->ir_error != 0 || req->ir_compcode != 0) { 8141170c2feSWarner Losh device_printf(sc->ipmi_dev, "Power cycling via IPMI failed code %#x %#x\n", 8151170c2feSWarner Losh req->ir_error, req->ir_compcode); 8161170c2feSWarner Losh return; 8171170c2feSWarner Losh } 8181170c2feSWarner Losh 8191170c2feSWarner Losh /* 8206d9d4b2dSEugene Grosbein * BMCs are notoriously slow, give it cycle_wait seconds for the power 8211170c2feSWarner Losh * down leg of the power cycle. If that fails, fallback to the next 8221170c2feSWarner Losh * hanlder in the shutdown_final chain and/or the platform failsafe. 8231170c2feSWarner Losh */ 82416f0063eSWarner Losh DELAY(cycle_wait * 1000 * 1000); 8251170c2feSWarner Losh device_printf(sc->ipmi_dev, "Power cycling via IPMI timed out\n"); 8261170c2feSWarner Losh } 8271170c2feSWarner Losh 8281170c2feSWarner Losh static void 829d72a0786SJohn Baldwin ipmi_startup(void *arg) 830d72a0786SJohn Baldwin { 831d72a0786SJohn Baldwin struct ipmi_softc *sc = arg; 832d72a0786SJohn Baldwin struct ipmi_request *req; 833d72a0786SJohn Baldwin device_t dev; 834d72a0786SJohn Baldwin int error, i; 835d72a0786SJohn Baldwin 836d72a0786SJohn Baldwin config_intrhook_disestablish(&sc->ipmi_ich); 837d72a0786SJohn Baldwin dev = sc->ipmi_dev; 838d72a0786SJohn Baldwin 839d72a0786SJohn Baldwin /* Initialize interface-independent state. */ 840c869aa71SJohn Baldwin mtx_init(&sc->ipmi_requests_lock, "ipmi requests", NULL, MTX_DEF); 841c869aa71SJohn Baldwin mtx_init(&sc->ipmi_io_lock, "ipmi io", NULL, MTX_DEF); 842d72a0786SJohn Baldwin cv_init(&sc->ipmi_request_added, "ipmireq"); 843f0f3e3e9SChuck Silvers TAILQ_INIT(&sc->ipmi_pending_requests_highpri); 844d72a0786SJohn Baldwin TAILQ_INIT(&sc->ipmi_pending_requests); 845d72a0786SJohn Baldwin 846d72a0786SJohn Baldwin /* Initialize interface-dependent state. */ 847d72a0786SJohn Baldwin error = sc->ipmi_startup(sc); 848d72a0786SJohn Baldwin if (error) { 849d72a0786SJohn Baldwin device_printf(dev, "Failed to initialize interface: %d\n", 850d72a0786SJohn Baldwin error); 851d72a0786SJohn Baldwin return; 852d72a0786SJohn Baldwin } 853d72a0786SJohn Baldwin 854d72a0786SJohn Baldwin /* Send a GET_DEVICE_ID request. */ 855c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 856d72a0786SJohn Baldwin IPMI_GET_DEVICE_ID, 0, 15); 857d72a0786SJohn Baldwin 858d72a0786SJohn Baldwin error = ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); 859d72a0786SJohn Baldwin if (error == EWOULDBLOCK) { 860d72a0786SJohn Baldwin device_printf(dev, "Timed out waiting for GET_DEVICE_ID\n"); 861d72a0786SJohn Baldwin return; 862d72a0786SJohn Baldwin } else if (error) { 863d72a0786SJohn Baldwin device_printf(dev, "Failed GET_DEVICE_ID: %d\n", error); 864d72a0786SJohn Baldwin return; 865d72a0786SJohn Baldwin } else if (req->ir_compcode != 0) { 866d72a0786SJohn Baldwin device_printf(dev, 867d72a0786SJohn Baldwin "Bad completion code for GET_DEVICE_ID: %d\n", 868d72a0786SJohn Baldwin req->ir_compcode); 869d72a0786SJohn Baldwin return; 870d72a0786SJohn Baldwin } else if (req->ir_replylen < 5) { 871d72a0786SJohn Baldwin device_printf(dev, "Short reply for GET_DEVICE_ID: %d\n", 872d72a0786SJohn Baldwin req->ir_replylen); 873d72a0786SJohn Baldwin return; 874d72a0786SJohn Baldwin } 875d72a0786SJohn Baldwin 87614689886SRuslan Ermilov device_printf(dev, "IPMI device rev. %d, firmware rev. %d.%d%d, " 8771170c2feSWarner Losh "version %d.%d, device support mask %#x\n", 878d72a0786SJohn Baldwin req->ir_reply[1] & 0x0f, 87914689886SRuslan Ermilov req->ir_reply[2] & 0x7f, req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f, 8801170c2feSWarner Losh req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4, req->ir_reply[5]); 8811170c2feSWarner Losh 8821170c2feSWarner Losh sc->ipmi_dev_support = req->ir_reply[5]; 883d72a0786SJohn Baldwin 884c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 885d72a0786SJohn Baldwin IPMI_CLEAR_FLAGS, 1, 0); 886d72a0786SJohn Baldwin 887d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req, 0); 888d72a0786SJohn Baldwin 889d72a0786SJohn Baldwin /* XXX: Magic numbers */ 890d72a0786SJohn Baldwin if (req->ir_compcode == 0xc0) { 891d72a0786SJohn Baldwin device_printf(dev, "Clear flags is busy\n"); 892d72a0786SJohn Baldwin } 893d72a0786SJohn Baldwin if (req->ir_compcode == 0xc1) { 894d72a0786SJohn Baldwin device_printf(dev, "Clear flags illegal\n"); 895d72a0786SJohn Baldwin } 896d72a0786SJohn Baldwin 897d72a0786SJohn Baldwin for (i = 0; i < 8; i++) { 898c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 899d72a0786SJohn Baldwin IPMI_GET_CHANNEL_INFO, 1, 0); 900d72a0786SJohn Baldwin req->ir_request[0] = i; 901d72a0786SJohn Baldwin 9029d3b47abSAlexander Motin error = ipmi_submit_driver_request(sc, req, 0); 903d72a0786SJohn Baldwin 9049d3b47abSAlexander Motin if (error != 0 || req->ir_compcode != 0) 905d72a0786SJohn Baldwin break; 906d72a0786SJohn Baldwin } 907d72a0786SJohn Baldwin device_printf(dev, "Number of channels %d\n", i); 908d72a0786SJohn Baldwin 9099662eef5SJohn Baldwin /* 9109662eef5SJohn Baldwin * Probe for watchdog, but only for backends which support 9119662eef5SJohn Baldwin * polled driver requests. 9129662eef5SJohn Baldwin */ 913e3500c60SWojciech Macek if (wd_init_enable && sc->ipmi_driver_requests_polled) { 914c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 915d72a0786SJohn Baldwin IPMI_GET_WDOG, 0, 0); 916d72a0786SJohn Baldwin 9179d3b47abSAlexander Motin error = ipmi_submit_driver_request(sc, req, 0); 918d72a0786SJohn Baldwin 9199d3b47abSAlexander Motin if (error == 0 && req->ir_compcode == 0x00) { 920d72a0786SJohn Baldwin device_printf(dev, "Attached watchdog\n"); 921d72a0786SJohn Baldwin /* register the watchdog event handler */ 9229662eef5SJohn Baldwin sc->ipmi_watchdog_tag = EVENTHANDLER_REGISTER( 9239662eef5SJohn Baldwin watchdog_list, ipmi_wd_event, sc, 0); 92414d00450SWarner Losh sc->ipmi_shutdown_tag = EVENTHANDLER_REGISTER( 92514d00450SWarner Losh shutdown_pre_sync, ipmi_shutdown_event, 92614d00450SWarner Losh sc, 0); 9279662eef5SJohn Baldwin } 928d72a0786SJohn Baldwin } 929d72a0786SJohn Baldwin 930943bebd2SJohn Baldwin sc->ipmi_cdev = make_dev(&ipmi_cdevsw, device_get_unit(dev), 931d72a0786SJohn Baldwin UID_ROOT, GID_OPERATOR, 0660, "ipmi%d", device_get_unit(dev)); 932943bebd2SJohn Baldwin if (sc->ipmi_cdev == NULL) { 933d72a0786SJohn Baldwin device_printf(dev, "Failed to create cdev\n"); 934d72a0786SJohn Baldwin return; 935d72a0786SJohn Baldwin } 936943bebd2SJohn Baldwin sc->ipmi_cdev->si_drv1 = sc; 9371170c2feSWarner Losh 9381170c2feSWarner Losh /* 93914d00450SWarner Losh * Set initial watchdog state. If desired, set an initial 94014d00450SWarner Losh * watchdog on startup. Or, if the watchdog device is 94114d00450SWarner Losh * disabled, clear any existing watchdog. 94214d00450SWarner Losh */ 94314d00450SWarner Losh if (on && wd_startup_countdown > 0) { 94414d00450SWarner Losh if (ipmi_set_watchdog(sc, wd_startup_countdown) == 0 && 94514d00450SWarner Losh ipmi_reset_watchdog(sc) == 0) { 94614d00450SWarner Losh sc->ipmi_watchdog_active = wd_startup_countdown; 94714d00450SWarner Losh sc->ipmi_watchdog_actions = wd_timer_actions; 94814d00450SWarner Losh sc->ipmi_watchdog_pretimeout = wd_pretimeout_countdown; 94914d00450SWarner Losh } else 95014d00450SWarner Losh (void)ipmi_set_watchdog(sc, 0); 95114d00450SWarner Losh ipmi_reset_watchdog(sc); 95214d00450SWarner Losh } else if (!on) 95314d00450SWarner Losh (void)ipmi_set_watchdog(sc, 0); 95414d00450SWarner Losh /* 95526649bb5SConrad Meyer * Power cycle the system off using IPMI. We use last - 2 since we don't 9561170c2feSWarner Losh * handle all the other kinds of reboots. We'll let others handle them. 9571170c2feSWarner Losh * We only try to do this if the BMC supports the Chassis device. 9581170c2feSWarner Losh */ 9591170c2feSWarner Losh if (sc->ipmi_dev_support & IPMI_ADS_CHASSIS) { 9601170c2feSWarner Losh device_printf(dev, "Establishing power cycle handler\n"); 9611170c2feSWarner Losh sc->ipmi_power_cycle_tag = EVENTHANDLER_REGISTER(shutdown_final, 96226649bb5SConrad Meyer ipmi_power_cycle, sc, SHUTDOWN_PRI_LAST - 2); 9631170c2feSWarner Losh } 964d72a0786SJohn Baldwin } 965d72a0786SJohn Baldwin 96637b1ce13SDoug Ambrisko int 96737b1ce13SDoug Ambrisko ipmi_attach(device_t dev) 96837b1ce13SDoug Ambrisko { 96937b1ce13SDoug Ambrisko struct ipmi_softc *sc = device_get_softc(dev); 970d72a0786SJohn Baldwin int error; 97137b1ce13SDoug Ambrisko 972d72a0786SJohn Baldwin if (sc->ipmi_irq_res != NULL && sc->ipmi_intr != NULL) { 973d72a0786SJohn Baldwin error = bus_setup_intr(dev, sc->ipmi_irq_res, INTR_TYPE_MISC, 974ef544f63SPaolo Pisati NULL, sc->ipmi_intr, sc, &sc->ipmi_irq); 975d72a0786SJohn Baldwin if (error) { 976d72a0786SJohn Baldwin device_printf(dev, "can't set up interrupt\n"); 977d72a0786SJohn Baldwin return (error); 97837b1ce13SDoug Ambrisko } 97937b1ce13SDoug Ambrisko } 98037b1ce13SDoug Ambrisko 981d72a0786SJohn Baldwin bzero(&sc->ipmi_ich, sizeof(struct intr_config_hook)); 982d72a0786SJohn Baldwin sc->ipmi_ich.ich_func = ipmi_startup; 983d72a0786SJohn Baldwin sc->ipmi_ich.ich_arg = sc; 984d72a0786SJohn Baldwin if (config_intrhook_establish(&sc->ipmi_ich) != 0) { 985d72a0786SJohn Baldwin device_printf(dev, "can't establish configuration hook\n"); 986d72a0786SJohn Baldwin return (ENOMEM); 98737b1ce13SDoug Ambrisko } 98837b1ce13SDoug Ambrisko 98937b1ce13SDoug Ambrisko ipmi_attached = 1; 990d72a0786SJohn Baldwin return (0); 99137b1ce13SDoug Ambrisko } 99237b1ce13SDoug Ambrisko 99337b1ce13SDoug Ambrisko int 99437b1ce13SDoug Ambrisko ipmi_detach(device_t dev) 99537b1ce13SDoug Ambrisko { 99637b1ce13SDoug Ambrisko struct ipmi_softc *sc; 99737b1ce13SDoug Ambrisko 99837b1ce13SDoug Ambrisko sc = device_get_softc(dev); 999d72a0786SJohn Baldwin 1000d72a0786SJohn Baldwin /* Fail if there are any open handles. */ 1001d72a0786SJohn Baldwin IPMI_LOCK(sc); 1002943bebd2SJohn Baldwin if (sc->ipmi_opened) { 1003d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 1004d72a0786SJohn Baldwin return (EBUSY); 1005d72a0786SJohn Baldwin } 1006d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 1007943bebd2SJohn Baldwin if (sc->ipmi_cdev) 1008943bebd2SJohn Baldwin destroy_dev(sc->ipmi_cdev); 1009d72a0786SJohn Baldwin 1010d72a0786SJohn Baldwin /* Detach from watchdog handling and turn off watchdog. */ 101114d00450SWarner Losh if (sc->ipmi_shutdown_tag) 101214d00450SWarner Losh EVENTHANDLER_DEREGISTER(shutdown_pre_sync, 101314d00450SWarner Losh sc->ipmi_shutdown_tag); 1014d72a0786SJohn Baldwin if (sc->ipmi_watchdog_tag) { 1015d72a0786SJohn Baldwin EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_watchdog_tag); 1016d72a0786SJohn Baldwin ipmi_set_watchdog(sc, 0); 1017d72a0786SJohn Baldwin } 1018d72a0786SJohn Baldwin 10191170c2feSWarner Losh /* Detach from shutdown handling for power cycle reboot */ 10201170c2feSWarner Losh if (sc->ipmi_power_cycle_tag) 10211170c2feSWarner Losh EVENTHANDLER_DEREGISTER(shutdown_final, sc->ipmi_power_cycle_tag); 10221170c2feSWarner Losh 1023d72a0786SJohn Baldwin /* XXX: should use shutdown callout I think. */ 1024d72a0786SJohn Baldwin /* If the backend uses a kthread, shut it down. */ 1025d72a0786SJohn Baldwin IPMI_LOCK(sc); 1026d72a0786SJohn Baldwin sc->ipmi_detaching = 1; 1027d72a0786SJohn Baldwin if (sc->ipmi_kthread) { 1028d72a0786SJohn Baldwin cv_broadcast(&sc->ipmi_request_added); 1029c869aa71SJohn Baldwin msleep(sc->ipmi_kthread, &sc->ipmi_requests_lock, 0, 1030c869aa71SJohn Baldwin "ipmi_wait", 0); 1031d72a0786SJohn Baldwin } 1032d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 1033d72a0786SJohn Baldwin if (sc->ipmi_irq) 1034d72a0786SJohn Baldwin bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); 1035d72a0786SJohn Baldwin 1036d72a0786SJohn Baldwin ipmi_release_resources(dev); 1037c869aa71SJohn Baldwin mtx_destroy(&sc->ipmi_io_lock); 1038c869aa71SJohn Baldwin mtx_destroy(&sc->ipmi_requests_lock); 1039d72a0786SJohn Baldwin return (0); 1040d72a0786SJohn Baldwin } 1041d72a0786SJohn Baldwin 1042d72a0786SJohn Baldwin void 1043d72a0786SJohn Baldwin ipmi_release_resources(device_t dev) 1044d72a0786SJohn Baldwin { 1045d72a0786SJohn Baldwin struct ipmi_softc *sc; 1046d72a0786SJohn Baldwin int i; 1047d72a0786SJohn Baldwin 1048d72a0786SJohn Baldwin sc = device_get_softc(dev); 1049d72a0786SJohn Baldwin if (sc->ipmi_irq) 1050d72a0786SJohn Baldwin bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); 1051d72a0786SJohn Baldwin if (sc->ipmi_irq_res) 1052d72a0786SJohn Baldwin bus_release_resource(dev, SYS_RES_IRQ, sc->ipmi_irq_rid, 1053d72a0786SJohn Baldwin sc->ipmi_irq_res); 1054d72a0786SJohn Baldwin for (i = 0; i < MAX_RES; i++) 1055d72a0786SJohn Baldwin if (sc->ipmi_io_res[i]) 1056d72a0786SJohn Baldwin bus_release_resource(dev, sc->ipmi_io_type, 1057d72a0786SJohn Baldwin sc->ipmi_io_rid + i, sc->ipmi_io_res[i]); 1058d72a0786SJohn Baldwin } 1059d72a0786SJohn Baldwin 1060d72a0786SJohn Baldwin /* XXX: Why? */ 1061d72a0786SJohn Baldwin static void 1062d72a0786SJohn Baldwin ipmi_unload(void *arg) 1063d72a0786SJohn Baldwin { 1064d72a0786SJohn Baldwin device_t * devs; 1065d72a0786SJohn Baldwin int count; 1066d72a0786SJohn Baldwin int i; 1067d72a0786SJohn Baldwin 1068ac56d90aSJohn Baldwin if (devclass_get_devices(devclass_find("ipmi"), &devs, &count) != 0) 1069bec0c98eSJohn Baldwin return; 1070d72a0786SJohn Baldwin for (i = 0; i < count; i++) 1071d72a0786SJohn Baldwin device_delete_child(device_get_parent(devs[i]), devs[i]); 1072bec0c98eSJohn Baldwin free(devs, M_TEMP); 1073d72a0786SJohn Baldwin } 1074d72a0786SJohn Baldwin SYSUNINIT(ipmi_unload, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipmi_unload, NULL); 1075d72a0786SJohn Baldwin 107619d61f3fSDoug Ambrisko #ifdef IMPI_DEBUG 107737b1ce13SDoug Ambrisko static void 1078d72a0786SJohn Baldwin dump_buf(u_char *data, int len) 1079d72a0786SJohn Baldwin { 108037b1ce13SDoug Ambrisko char buf[20]; 108137b1ce13SDoug Ambrisko char line[1024]; 108237b1ce13SDoug Ambrisko char temp[30]; 108337b1ce13SDoug Ambrisko int count = 0; 108437b1ce13SDoug Ambrisko int i=0; 108537b1ce13SDoug Ambrisko 108637b1ce13SDoug Ambrisko printf("Address %p len %d\n", data, len); 108737b1ce13SDoug Ambrisko if (len > 256) 108837b1ce13SDoug Ambrisko len = 256; 108937b1ce13SDoug Ambrisko line[0] = '\000'; 109037b1ce13SDoug Ambrisko for (; len > 0; len--, data++) { 109137b1ce13SDoug Ambrisko sprintf(temp, "%02x ", *data); 109237b1ce13SDoug Ambrisko strcat(line, temp); 109337b1ce13SDoug Ambrisko if (*data >= ' ' && *data <= '~') 109437b1ce13SDoug Ambrisko buf[count] = *data; 109537b1ce13SDoug Ambrisko else if (*data >= 'A' && *data <= 'Z') 109637b1ce13SDoug Ambrisko buf[count] = *data; 109737b1ce13SDoug Ambrisko else 109837b1ce13SDoug Ambrisko buf[count] = '.'; 109937b1ce13SDoug Ambrisko if (++count == 16) { 110037b1ce13SDoug Ambrisko buf[count] = '\000'; 110137b1ce13SDoug Ambrisko count = 0; 110237b1ce13SDoug Ambrisko printf(" %3x %s %s\n", i, line, buf); 110337b1ce13SDoug Ambrisko i+=16; 110437b1ce13SDoug Ambrisko line[0] = '\000'; 110537b1ce13SDoug Ambrisko } 110637b1ce13SDoug Ambrisko } 110737b1ce13SDoug Ambrisko buf[count] = '\000'; 110837b1ce13SDoug Ambrisko 110937b1ce13SDoug Ambrisko for (; count != 16; count++) { 111037b1ce13SDoug Ambrisko strcat(line, " "); 111137b1ce13SDoug Ambrisko } 111237b1ce13SDoug Ambrisko printf(" %3x %s %s\n", i, line, buf); 111337b1ce13SDoug Ambrisko } 111437b1ce13SDoug Ambrisko #endif 1115