137b1ce13SDoug Ambrisko /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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/cdefs.h> 3037b1ce13SDoug Ambrisko __FBSDID("$FreeBSD$"); 3137b1ce13SDoug Ambrisko 3237b1ce13SDoug Ambrisko #include <sys/param.h> 3337b1ce13SDoug Ambrisko #include <sys/systm.h> 34d72a0786SJohn Baldwin #include <sys/bus.h> 35d72a0786SJohn Baldwin #include <sys/condvar.h> 36d72a0786SJohn Baldwin #include <sys/conf.h> 37e2e050c8SConrad Meyer #include <sys/eventhandler.h> 3837b1ce13SDoug Ambrisko #include <sys/kernel.h> 39e2e050c8SConrad Meyer #include <sys/lock.h> 4037b1ce13SDoug Ambrisko #include <sys/malloc.h> 4137b1ce13SDoug Ambrisko #include <sys/module.h> 42e2e050c8SConrad Meyer #include <sys/mutex.h> 43d72a0786SJohn Baldwin #include <sys/poll.h> 441170c2feSWarner Losh #include <sys/reboot.h> 4537b1ce13SDoug Ambrisko #include <sys/rman.h> 46d72a0786SJohn Baldwin #include <sys/selinfo.h> 4737b1ce13SDoug Ambrisko #include <sys/sysctl.h> 48d72a0786SJohn Baldwin #include <sys/watchdog.h> 4937b1ce13SDoug Ambrisko 5037b1ce13SDoug Ambrisko #ifdef LOCAL_MODULE 5137b1ce13SDoug Ambrisko #include <ipmi.h> 5237b1ce13SDoug Ambrisko #include <ipmivars.h> 5337b1ce13SDoug Ambrisko #else 5437b1ce13SDoug Ambrisko #include <sys/ipmi.h> 5537b1ce13SDoug Ambrisko #include <dev/ipmi/ipmivars.h> 5637b1ce13SDoug Ambrisko #endif 5737b1ce13SDoug Ambrisko 58562894f0SBrooks Davis #ifdef IPMICTL_SEND_COMMAND_32 59562894f0SBrooks Davis #include <sys/abi_compat.h> 60562894f0SBrooks Davis #endif 61562894f0SBrooks Davis 62c869aa71SJohn Baldwin /* 63c869aa71SJohn Baldwin * Driver request structures are allocated on the stack via alloca() to 64c869aa71SJohn Baldwin * avoid calling malloc(), especially for the watchdog handler. 65c869aa71SJohn Baldwin * To avoid too much stack growth, a previously allocated structure can 66c869aa71SJohn Baldwin * be reused via IPMI_INIT_DRIVER_REQUEST(), but the caller should ensure 67c869aa71SJohn Baldwin * that there is adequate reply/request space in the original allocation. 68c869aa71SJohn Baldwin */ 69c869aa71SJohn Baldwin #define IPMI_INIT_DRIVER_REQUEST(req, addr, cmd, reqlen, replylen) \ 70c869aa71SJohn Baldwin bzero((req), sizeof(struct ipmi_request)); \ 71c869aa71SJohn Baldwin ipmi_init_request((req), NULL, 0, (addr), (cmd), (reqlen), (replylen)) 72c869aa71SJohn Baldwin 73c869aa71SJohn Baldwin #define IPMI_ALLOC_DRIVER_REQUEST(req, addr, cmd, reqlen, replylen) \ 74c869aa71SJohn Baldwin (req) = __builtin_alloca(sizeof(struct ipmi_request) + \ 75c869aa71SJohn Baldwin (reqlen) + (replylen)); \ 76c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST((req), (addr), (cmd), (reqlen), \ 77c869aa71SJohn Baldwin (replylen)) 78c869aa71SJohn Baldwin 7937b1ce13SDoug Ambrisko #ifdef IPMB 8037b1ce13SDoug Ambrisko static int ipmi_ipmb_checksum(u_char, int); 8137b1ce13SDoug Ambrisko static int ipmi_ipmb_send_message(device_t, u_char, u_char, u_char, 8237b1ce13SDoug Ambrisko u_char, u_char, int) 8337b1ce13SDoug Ambrisko #endif 8437b1ce13SDoug Ambrisko 8537b1ce13SDoug Ambrisko static d_ioctl_t ipmi_ioctl; 8637b1ce13SDoug Ambrisko static d_poll_t ipmi_poll; 8737b1ce13SDoug Ambrisko static d_open_t ipmi_open; 88943bebd2SJohn Baldwin static void ipmi_dtor(void *arg); 8937b1ce13SDoug Ambrisko 9037b1ce13SDoug Ambrisko int ipmi_attached = 0; 9137b1ce13SDoug Ambrisko 9237b1ce13SDoug Ambrisko static int on = 1; 9314d00450SWarner Losh static bool wd_in_shutdown = false; 9414d00450SWarner Losh static int wd_timer_actions = IPMI_SET_WD_ACTION_POWER_CYCLE; 959ee3ea71SPeter Wemm static int wd_shutdown_countdown = 0; /* sec */ 96c154763dSWarner Losh static int wd_startup_countdown = 0; /* sec */ 9714d00450SWarner Losh static int wd_pretimeout_countdown = 120; /* sec */ 9816f0063eSWarner Losh static int cycle_wait = 10; /* sec */ 9914d00450SWarner Losh 1007029da5cSPawel Biernacki static SYSCTL_NODE(_hw, OID_AUTO, ipmi, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 1016472ac3dSEd Schouten "IPMI driver parameters"); 10214d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RWTUN, 10337b1ce13SDoug Ambrisko &on, 0, ""); 10414d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_timer_actions, CTLFLAG_RW, 10514d00450SWarner Losh &wd_timer_actions, 0, 10614d00450SWarner Losh "IPMI watchdog timer actions (including pre-timeout interrupt)"); 10714d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_shutdown_countdown, CTLFLAG_RW, 10814d00450SWarner Losh &wd_shutdown_countdown, 0, 10914d00450SWarner Losh "IPMI watchdog countdown for shutdown (seconds)"); 11014d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_startup_countdown, CTLFLAG_RDTUN, 11114d00450SWarner Losh &wd_startup_countdown, 0, 11214d00450SWarner Losh "IPMI watchdog countdown initialized during startup (seconds)"); 11314d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_pretimeout_countdown, CTLFLAG_RW, 11414d00450SWarner Losh &wd_pretimeout_countdown, 0, 11514d00450SWarner Losh "IPMI watchdog pre-timeout countdown (seconds)"); 11616f0063eSWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, cyle_wait, CTLFLAG_RWTUN, 11716f0063eSWarner Losh &cycle_wait, 0, 11816f0063eSWarner Losh "IPMI power cycle on reboot delay time (seconds)"); 11937b1ce13SDoug Ambrisko 12037b1ce13SDoug Ambrisko static struct cdevsw ipmi_cdevsw = { 12137b1ce13SDoug Ambrisko .d_version = D_VERSION, 12237b1ce13SDoug Ambrisko .d_open = ipmi_open, 12337b1ce13SDoug Ambrisko .d_ioctl = ipmi_ioctl, 12437b1ce13SDoug Ambrisko .d_poll = ipmi_poll, 12537b1ce13SDoug Ambrisko .d_name = "ipmi", 12637b1ce13SDoug Ambrisko }; 12737b1ce13SDoug Ambrisko 128d745c852SEd Schouten static MALLOC_DEFINE(M_IPMI, "ipmi", "ipmi"); 12937b1ce13SDoug Ambrisko 13037b1ce13SDoug Ambrisko static int 131d72a0786SJohn Baldwin ipmi_open(struct cdev *cdev, int flags, int fmt, struct thread *td) 13237b1ce13SDoug Ambrisko { 133d72a0786SJohn Baldwin struct ipmi_device *dev; 13437b1ce13SDoug Ambrisko struct ipmi_softc *sc; 135943bebd2SJohn Baldwin int error; 13637b1ce13SDoug Ambrisko 13737b1ce13SDoug Ambrisko if (!on) 138d72a0786SJohn Baldwin return (ENOENT); 13937b1ce13SDoug Ambrisko 140943bebd2SJohn Baldwin /* Initialize the per file descriptor data. */ 141943bebd2SJohn Baldwin dev = malloc(sizeof(struct ipmi_device), M_IPMI, M_WAITOK | M_ZERO); 142943bebd2SJohn Baldwin error = devfs_set_cdevpriv(dev, ipmi_dtor); 143943bebd2SJohn Baldwin if (error) { 144943bebd2SJohn Baldwin free(dev, M_IPMI); 145943bebd2SJohn Baldwin return (error); 14637b1ce13SDoug Ambrisko } 147943bebd2SJohn Baldwin 148943bebd2SJohn Baldwin sc = cdev->si_drv1; 149943bebd2SJohn Baldwin TAILQ_INIT(&dev->ipmi_completed_requests); 150943bebd2SJohn Baldwin dev->ipmi_address = IPMI_BMC_SLAVE_ADDR; 151943bebd2SJohn Baldwin dev->ipmi_lun = IPMI_BMC_SMS_LUN; 152943bebd2SJohn Baldwin dev->ipmi_softc = sc; 153943bebd2SJohn Baldwin IPMI_LOCK(sc); 154943bebd2SJohn Baldwin sc->ipmi_opened++; 155d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 15637b1ce13SDoug Ambrisko 157d72a0786SJohn Baldwin return (0); 15837b1ce13SDoug Ambrisko } 15937b1ce13SDoug Ambrisko 16037b1ce13SDoug Ambrisko static int 161d72a0786SJohn Baldwin ipmi_poll(struct cdev *cdev, int poll_events, struct thread *td) 16237b1ce13SDoug Ambrisko { 163d72a0786SJohn Baldwin struct ipmi_device *dev; 16437b1ce13SDoug Ambrisko struct ipmi_softc *sc; 16537b1ce13SDoug Ambrisko int revents = 0; 16637b1ce13SDoug Ambrisko 167943bebd2SJohn Baldwin if (devfs_get_cdevpriv((void **)&dev)) 168943bebd2SJohn Baldwin return (0); 16937b1ce13SDoug Ambrisko 170943bebd2SJohn Baldwin sc = cdev->si_drv1; 171d72a0786SJohn Baldwin IPMI_LOCK(sc); 17237b1ce13SDoug Ambrisko if (poll_events & (POLLIN | POLLRDNORM)) { 173d72a0786SJohn Baldwin if (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) 17437b1ce13SDoug Ambrisko revents |= poll_events & (POLLIN | POLLRDNORM); 175d72a0786SJohn Baldwin if (dev->ipmi_requests == 0) 17637b1ce13SDoug Ambrisko revents |= POLLERR; 17737b1ce13SDoug Ambrisko } 17837b1ce13SDoug Ambrisko 17937b1ce13SDoug Ambrisko if (revents == 0) { 18037b1ce13SDoug Ambrisko if (poll_events & (POLLIN | POLLRDNORM)) 181d72a0786SJohn Baldwin selrecord(td, &dev->ipmi_select); 182d72a0786SJohn Baldwin } 183d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 184d72a0786SJohn Baldwin 185d72a0786SJohn Baldwin return (revents); 18637b1ce13SDoug Ambrisko } 18737b1ce13SDoug Ambrisko 188d72a0786SJohn Baldwin static void 189d72a0786SJohn Baldwin ipmi_purge_completed_requests(struct ipmi_device *dev) 190d72a0786SJohn Baldwin { 191d72a0786SJohn Baldwin struct ipmi_request *req; 192d72a0786SJohn Baldwin 193d72a0786SJohn Baldwin while (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) { 194d72a0786SJohn Baldwin req = TAILQ_FIRST(&dev->ipmi_completed_requests); 195d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, req, ir_link); 196d72a0786SJohn Baldwin dev->ipmi_requests--; 197d72a0786SJohn Baldwin ipmi_free_request(req); 198d72a0786SJohn Baldwin } 19937b1ce13SDoug Ambrisko } 20037b1ce13SDoug Ambrisko 201943bebd2SJohn Baldwin static void 202943bebd2SJohn Baldwin ipmi_dtor(void *arg) 20337b1ce13SDoug Ambrisko { 204d72a0786SJohn Baldwin struct ipmi_request *req, *nreq; 205d72a0786SJohn Baldwin struct ipmi_device *dev; 20637b1ce13SDoug Ambrisko struct ipmi_softc *sc; 20737b1ce13SDoug Ambrisko 208943bebd2SJohn Baldwin dev = arg; 209d72a0786SJohn Baldwin sc = dev->ipmi_softc; 21037b1ce13SDoug Ambrisko 211d72a0786SJohn Baldwin IPMI_LOCK(sc); 212d72a0786SJohn Baldwin if (dev->ipmi_requests) { 213d72a0786SJohn Baldwin /* Throw away any pending requests for this device. */ 214d72a0786SJohn Baldwin TAILQ_FOREACH_SAFE(req, &sc->ipmi_pending_requests, ir_link, 215d72a0786SJohn Baldwin nreq) { 216d72a0786SJohn Baldwin if (req->ir_owner == dev) { 217d72a0786SJohn Baldwin TAILQ_REMOVE(&sc->ipmi_pending_requests, req, 218d72a0786SJohn Baldwin ir_link); 219d72a0786SJohn Baldwin dev->ipmi_requests--; 220d72a0786SJohn Baldwin ipmi_free_request(req); 221d72a0786SJohn Baldwin } 222d72a0786SJohn Baldwin } 22337b1ce13SDoug Ambrisko 224d72a0786SJohn Baldwin /* Throw away any pending completed requests for this device. */ 225d72a0786SJohn Baldwin ipmi_purge_completed_requests(dev); 226d72a0786SJohn Baldwin 227d72a0786SJohn Baldwin /* 228d72a0786SJohn Baldwin * If we still have outstanding requests, they must be stuck 229d72a0786SJohn Baldwin * in an interface driver, so wait for those to drain. 230d72a0786SJohn Baldwin */ 231d72a0786SJohn Baldwin dev->ipmi_closing = 1; 232d72a0786SJohn Baldwin while (dev->ipmi_requests > 0) { 233c869aa71SJohn Baldwin msleep(&dev->ipmi_requests, &sc->ipmi_requests_lock, 234c869aa71SJohn Baldwin PWAIT, "ipmidrain", 0); 235d72a0786SJohn Baldwin ipmi_purge_completed_requests(dev); 236d72a0786SJohn Baldwin } 237d72a0786SJohn Baldwin } 238943bebd2SJohn Baldwin sc->ipmi_opened--; 239d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 240d72a0786SJohn Baldwin 241d72a0786SJohn Baldwin /* Cleanup. */ 242d72a0786SJohn Baldwin free(dev, M_IPMI); 24337b1ce13SDoug Ambrisko } 24437b1ce13SDoug Ambrisko 24537b1ce13SDoug Ambrisko #ifdef IPMB 24637b1ce13SDoug Ambrisko static int 24737b1ce13SDoug Ambrisko ipmi_ipmb_checksum(u_char *data, int len) 24837b1ce13SDoug Ambrisko { 24937b1ce13SDoug Ambrisko u_char sum = 0; 25037b1ce13SDoug Ambrisko 25137b1ce13SDoug Ambrisko for (; len; len--) { 25237b1ce13SDoug Ambrisko sum += *data++; 25337b1ce13SDoug Ambrisko } 254d72a0786SJohn Baldwin return (-sum); 25537b1ce13SDoug Ambrisko } 25637b1ce13SDoug Ambrisko 257d72a0786SJohn Baldwin /* XXX: Needs work */ 25837b1ce13SDoug Ambrisko static int 25937b1ce13SDoug Ambrisko ipmi_ipmb_send_message(device_t dev, u_char channel, u_char netfn, 26037b1ce13SDoug Ambrisko u_char command, u_char seq, u_char *data, int data_len) 26137b1ce13SDoug Ambrisko { 26237b1ce13SDoug Ambrisko struct ipmi_softc *sc = device_get_softc(dev); 263d72a0786SJohn Baldwin struct ipmi_request *req; 26437b1ce13SDoug Ambrisko u_char slave_addr = 0x52; 265d72a0786SJohn Baldwin int error; 26637b1ce13SDoug Ambrisko 267c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 268d72a0786SJohn Baldwin IPMI_SEND_MSG, data_len + 8, 0); 269d72a0786SJohn Baldwin req->ir_request[0] = channel; 270d72a0786SJohn Baldwin req->ir_request[1] = slave_addr; 271d72a0786SJohn Baldwin req->ir_request[2] = IPMI_ADDR(netfn, 0); 272d72a0786SJohn Baldwin req->ir_request[3] = ipmi_ipmb_checksum(&req->ir_request[1], 2); 273d72a0786SJohn Baldwin req->ir_request[4] = sc->ipmi_address; 274d72a0786SJohn Baldwin req->ir_request[5] = IPMI_ADDR(seq, sc->ipmi_lun); 275d72a0786SJohn Baldwin req->ir_request[6] = command; 27637b1ce13SDoug Ambrisko 277d72a0786SJohn Baldwin bcopy(data, &req->ir_request[7], data_len); 278d72a0786SJohn Baldwin temp[data_len + 7] = ipmi_ipmb_checksum(&req->ir_request[4], 279d72a0786SJohn Baldwin data_len + 3); 28037b1ce13SDoug Ambrisko 281d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req); 282d72a0786SJohn Baldwin error = req->ir_error; 28337b1ce13SDoug Ambrisko 284d72a0786SJohn Baldwin return (error); 28537b1ce13SDoug Ambrisko } 28637b1ce13SDoug Ambrisko 28737b1ce13SDoug Ambrisko static int 288d72a0786SJohn Baldwin ipmi_handle_attn(struct ipmi_softc *sc) 28937b1ce13SDoug Ambrisko { 290d72a0786SJohn Baldwin struct ipmi_request *req; 29137b1ce13SDoug Ambrisko int error; 29237b1ce13SDoug Ambrisko 29337b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "BMC has a message\n"); 294c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 295d72a0786SJohn Baldwin IPMI_GET_MSG_FLAGS, 0, 1); 29637b1ce13SDoug Ambrisko 297d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req); 298d72a0786SJohn Baldwin 299d72a0786SJohn Baldwin if (req->ir_error == 0 && req->ir_compcode == 0) { 300d72a0786SJohn Baldwin if (req->ir_reply[0] & IPMI_MSG_BUFFER_FULL) { 30137b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "message buffer full"); 30237b1ce13SDoug Ambrisko } 303d72a0786SJohn Baldwin if (req->ir_reply[0] & IPMI_WDT_PRE_TIMEOUT) { 30437b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, 30537b1ce13SDoug Ambrisko "watchdog about to go off"); 30637b1ce13SDoug Ambrisko } 307d72a0786SJohn Baldwin if (req->ir_reply[0] & IPMI_MSG_AVAILABLE) { 308c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, 309d72a0786SJohn Baldwin IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG, 0, 310d72a0786SJohn Baldwin 16); 31137b1ce13SDoug Ambrisko 31237b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "throw out message "); 31337b1ce13SDoug Ambrisko dump_buf(temp, 16); 31437b1ce13SDoug Ambrisko } 31537b1ce13SDoug Ambrisko } 316d72a0786SJohn Baldwin error = req->ir_error; 317d72a0786SJohn Baldwin 318d72a0786SJohn Baldwin return (error); 319d72a0786SJohn Baldwin } 320d72a0786SJohn Baldwin #endif 321d72a0786SJohn Baldwin 32237b1ce13SDoug Ambrisko static int 323d72a0786SJohn Baldwin ipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, 32437b1ce13SDoug Ambrisko int flags, struct thread *td) 32537b1ce13SDoug Ambrisko { 32637b1ce13SDoug Ambrisko struct ipmi_softc *sc; 327d72a0786SJohn Baldwin struct ipmi_device *dev; 328d72a0786SJohn Baldwin struct ipmi_request *kreq; 32937b1ce13SDoug Ambrisko struct ipmi_req *req = (struct ipmi_req *)data; 33037b1ce13SDoug Ambrisko struct ipmi_recv *recv = (struct ipmi_recv *)data; 33137b1ce13SDoug Ambrisko struct ipmi_addr addr; 332d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 333d72a0786SJohn Baldwin struct ipmi_req32 *req32 = (struct ipmi_req32 *)data; 334d72a0786SJohn Baldwin struct ipmi_recv32 *recv32 = (struct ipmi_recv32 *)data; 335d72a0786SJohn Baldwin union { 336d72a0786SJohn Baldwin struct ipmi_req req; 337d72a0786SJohn Baldwin struct ipmi_recv recv; 338d72a0786SJohn Baldwin } thunk32; 339d72a0786SJohn Baldwin #endif 34037b1ce13SDoug Ambrisko int error, len; 34137b1ce13SDoug Ambrisko 342943bebd2SJohn Baldwin error = devfs_get_cdevpriv((void **)&dev); 343943bebd2SJohn Baldwin if (error) 344943bebd2SJohn Baldwin return (error); 345943bebd2SJohn Baldwin 346943bebd2SJohn Baldwin sc = cdev->si_drv1; 347d72a0786SJohn Baldwin 348d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 349d72a0786SJohn Baldwin /* Convert 32-bit structures to native. */ 350d72a0786SJohn Baldwin switch (cmd) { 351d72a0786SJohn Baldwin case IPMICTL_SEND_COMMAND_32: 352d72a0786SJohn Baldwin req = &thunk32.req; 353d72a0786SJohn Baldwin req->addr = PTRIN(req32->addr); 354d72a0786SJohn Baldwin req->addr_len = req32->addr_len; 355d72a0786SJohn Baldwin req->msgid = req32->msgid; 356d72a0786SJohn Baldwin req->msg.netfn = req32->msg.netfn; 357d72a0786SJohn Baldwin req->msg.cmd = req32->msg.cmd; 358d72a0786SJohn Baldwin req->msg.data_len = req32->msg.data_len; 359d72a0786SJohn Baldwin req->msg.data = PTRIN(req32->msg.data); 360d72a0786SJohn Baldwin break; 361d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 362d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 363d72a0786SJohn Baldwin recv = &thunk32.recv; 364d72a0786SJohn Baldwin recv->addr = PTRIN(recv32->addr); 365d72a0786SJohn Baldwin recv->addr_len = recv32->addr_len; 366d72a0786SJohn Baldwin recv->msg.data_len = recv32->msg.data_len; 367d72a0786SJohn Baldwin recv->msg.data = PTRIN(recv32->msg.data); 368d72a0786SJohn Baldwin break; 369d72a0786SJohn Baldwin } 370d72a0786SJohn Baldwin #endif 37137b1ce13SDoug Ambrisko 37237b1ce13SDoug Ambrisko switch (cmd) { 373d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 374d72a0786SJohn Baldwin case IPMICTL_SEND_COMMAND_32: 375d72a0786SJohn Baldwin #endif 37637b1ce13SDoug Ambrisko case IPMICTL_SEND_COMMAND: 377d72a0786SJohn Baldwin /* 378d72a0786SJohn Baldwin * XXX: Need to add proper handling of this. 379d72a0786SJohn Baldwin */ 38037b1ce13SDoug Ambrisko error = copyin(req->addr, &addr, sizeof(addr)); 381d72a0786SJohn Baldwin if (error) 382d72a0786SJohn Baldwin return (error); 383d72a0786SJohn Baldwin 384d72a0786SJohn Baldwin IPMI_LOCK(sc); 385d72a0786SJohn Baldwin /* clear out old stuff in queue of stuff done */ 386d72a0786SJohn Baldwin /* XXX: This seems odd. */ 387d72a0786SJohn Baldwin while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))) { 388d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, 389d72a0786SJohn Baldwin ir_link); 390d72a0786SJohn Baldwin dev->ipmi_requests--; 391d72a0786SJohn Baldwin ipmi_free_request(kreq); 39237b1ce13SDoug Ambrisko } 393d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 394d72a0786SJohn Baldwin 395d72a0786SJohn Baldwin kreq = ipmi_alloc_request(dev, req->msgid, 396d72a0786SJohn Baldwin IPMI_ADDR(req->msg.netfn, 0), req->msg.cmd, 397d72a0786SJohn Baldwin req->msg.data_len, IPMI_MAX_RX); 398d72a0786SJohn Baldwin error = copyin(req->msg.data, kreq->ir_request, 39937b1ce13SDoug Ambrisko req->msg.data_len); 400d72a0786SJohn Baldwin if (error) { 401d72a0786SJohn Baldwin ipmi_free_request(kreq); 402d72a0786SJohn Baldwin return (error); 40337b1ce13SDoug Ambrisko } 404d72a0786SJohn Baldwin IPMI_LOCK(sc); 405d72a0786SJohn Baldwin dev->ipmi_requests++; 406d72a0786SJohn Baldwin error = sc->ipmi_enqueue_request(sc, kreq); 407d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 408d72a0786SJohn Baldwin if (error) 409d72a0786SJohn Baldwin return (error); 410d72a0786SJohn Baldwin break; 411d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 412d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 413d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 414d72a0786SJohn Baldwin #endif 41537b1ce13SDoug Ambrisko case IPMICTL_RECEIVE_MSG_TRUNC: 41637b1ce13SDoug Ambrisko case IPMICTL_RECEIVE_MSG: 41737b1ce13SDoug Ambrisko error = copyin(recv->addr, &addr, sizeof(addr)); 418d72a0786SJohn Baldwin if (error) 419d72a0786SJohn Baldwin return (error); 420d72a0786SJohn Baldwin 421d72a0786SJohn Baldwin IPMI_LOCK(sc); 422d72a0786SJohn Baldwin kreq = TAILQ_FIRST(&dev->ipmi_completed_requests); 423d72a0786SJohn Baldwin if (kreq == NULL) { 424d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 425d72a0786SJohn Baldwin return (EAGAIN); 42637b1ce13SDoug Ambrisko } 427d72a0786SJohn Baldwin addr.channel = IPMI_BMC_CHANNEL; 428d72a0786SJohn Baldwin /* XXX */ 429d72a0786SJohn Baldwin recv->recv_type = IPMI_RESPONSE_RECV_TYPE; 430d72a0786SJohn Baldwin recv->msgid = kreq->ir_msgid; 431d72a0786SJohn Baldwin recv->msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2; 432d72a0786SJohn Baldwin recv->msg.cmd = kreq->ir_command; 433d72a0786SJohn Baldwin error = kreq->ir_error; 434d72a0786SJohn Baldwin if (error) { 435d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, 436d72a0786SJohn Baldwin ir_link); 437d72a0786SJohn Baldwin dev->ipmi_requests--; 438d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 439d72a0786SJohn Baldwin ipmi_free_request(kreq); 440d72a0786SJohn Baldwin return (error); 441d72a0786SJohn Baldwin } 442d72a0786SJohn Baldwin len = kreq->ir_replylen + 1; 443d72a0786SJohn Baldwin if (recv->msg.data_len < len && 444d72a0786SJohn Baldwin (cmd == IPMICTL_RECEIVE_MSG 445d72a0786SJohn Baldwin #ifdef IPMICTL_RECEIVE_MSG_32 446c12dbd1dSDavid E. O'Brien || cmd == IPMICTL_RECEIVE_MSG_32 447d72a0786SJohn Baldwin #endif 448d72a0786SJohn Baldwin )) { 449d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 450d72a0786SJohn Baldwin return (EMSGSIZE); 451d72a0786SJohn Baldwin } 452d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link); 453d72a0786SJohn Baldwin dev->ipmi_requests--; 454d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 45537b1ce13SDoug Ambrisko len = min(recv->msg.data_len, len); 45637b1ce13SDoug Ambrisko recv->msg.data_len = len; 45737b1ce13SDoug Ambrisko error = copyout(&addr, recv->addr,sizeof(addr)); 45837b1ce13SDoug Ambrisko if (error == 0) 459d72a0786SJohn Baldwin error = copyout(&kreq->ir_compcode, recv->msg.data, 1); 460d72a0786SJohn Baldwin if (error == 0) 461d72a0786SJohn Baldwin error = copyout(kreq->ir_reply, recv->msg.data + 1, 462d72a0786SJohn Baldwin len - 1); 463d72a0786SJohn Baldwin ipmi_free_request(kreq); 464d72a0786SJohn Baldwin if (error) 465d72a0786SJohn Baldwin return (error); 466d72a0786SJohn Baldwin break; 46737b1ce13SDoug Ambrisko case IPMICTL_SET_MY_ADDRESS_CMD: 468d72a0786SJohn Baldwin IPMI_LOCK(sc); 469d72a0786SJohn Baldwin dev->ipmi_address = *(int*)data; 470d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 471d72a0786SJohn Baldwin break; 47237b1ce13SDoug Ambrisko case IPMICTL_GET_MY_ADDRESS_CMD: 473d72a0786SJohn Baldwin IPMI_LOCK(sc); 474d72a0786SJohn Baldwin *(int*)data = dev->ipmi_address; 475d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 476d72a0786SJohn Baldwin break; 47737b1ce13SDoug Ambrisko case IPMICTL_SET_MY_LUN_CMD: 478d72a0786SJohn Baldwin IPMI_LOCK(sc); 479d72a0786SJohn Baldwin dev->ipmi_lun = *(int*)data & 0x3; 480d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 481d72a0786SJohn Baldwin break; 48237b1ce13SDoug Ambrisko case IPMICTL_GET_MY_LUN_CMD: 483d72a0786SJohn Baldwin IPMI_LOCK(sc); 484d72a0786SJohn Baldwin *(int*)data = dev->ipmi_lun; 485d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 486d72a0786SJohn Baldwin break; 48737b1ce13SDoug Ambrisko case IPMICTL_SET_GETS_EVENTS_CMD: 48837b1ce13SDoug Ambrisko /* 48937b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, 49037b1ce13SDoug Ambrisko "IPMICTL_SET_GETS_EVENTS_CMD NA\n"); 49137b1ce13SDoug Ambrisko */ 492d72a0786SJohn Baldwin break; 49337b1ce13SDoug Ambrisko case IPMICTL_REGISTER_FOR_CMD: 49437b1ce13SDoug Ambrisko case IPMICTL_UNREGISTER_FOR_CMD: 495d72a0786SJohn Baldwin return (EOPNOTSUPP); 496d72a0786SJohn Baldwin default: 49737b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "Unknown IOCTL %lX\n", cmd); 498d72a0786SJohn Baldwin return (ENOIOCTL); 49937b1ce13SDoug Ambrisko } 50037b1ce13SDoug Ambrisko 501d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 502d72a0786SJohn Baldwin /* Update changed fields in 32-bit structures. */ 503d72a0786SJohn Baldwin switch (cmd) { 504d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 505d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 506d72a0786SJohn Baldwin recv32->recv_type = recv->recv_type; 507d72a0786SJohn Baldwin recv32->msgid = recv->msgid; 508d72a0786SJohn Baldwin recv32->msg.netfn = recv->msg.netfn; 509d72a0786SJohn Baldwin recv32->msg.cmd = recv->msg.cmd; 510d72a0786SJohn Baldwin recv32->msg.data_len = recv->msg.data_len; 51137b1ce13SDoug Ambrisko break; 51237b1ce13SDoug Ambrisko } 513d72a0786SJohn Baldwin #endif 514d72a0786SJohn Baldwin return (0); 51537b1ce13SDoug Ambrisko } 51637b1ce13SDoug Ambrisko 517d72a0786SJohn Baldwin /* 518d72a0786SJohn Baldwin * Request management. 519d72a0786SJohn Baldwin */ 52037b1ce13SDoug Ambrisko 521c869aa71SJohn Baldwin static __inline void 522c869aa71SJohn Baldwin ipmi_init_request(struct ipmi_request *req, struct ipmi_device *dev, long msgid, 523c869aa71SJohn Baldwin uint8_t addr, uint8_t command, size_t requestlen, size_t replylen) 524d72a0786SJohn Baldwin { 525d72a0786SJohn Baldwin 526d72a0786SJohn Baldwin req->ir_owner = dev; 527d72a0786SJohn Baldwin req->ir_msgid = msgid; 528d72a0786SJohn Baldwin req->ir_addr = addr; 529d72a0786SJohn Baldwin req->ir_command = command; 530d72a0786SJohn Baldwin if (requestlen) { 531d72a0786SJohn Baldwin req->ir_request = (char *)&req[1]; 532d72a0786SJohn Baldwin req->ir_requestlen = requestlen; 533d72a0786SJohn Baldwin } 534d72a0786SJohn Baldwin if (replylen) { 535d72a0786SJohn Baldwin req->ir_reply = (char *)&req[1] + requestlen; 536d72a0786SJohn Baldwin req->ir_replybuflen = replylen; 537d72a0786SJohn Baldwin } 538c869aa71SJohn Baldwin } 539c869aa71SJohn Baldwin 540c869aa71SJohn Baldwin /* Allocate a new request with request and reply buffers. */ 541c869aa71SJohn Baldwin struct ipmi_request * 542c869aa71SJohn Baldwin ipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr, 543c869aa71SJohn Baldwin uint8_t command, size_t requestlen, size_t replylen) 544c869aa71SJohn Baldwin { 545c869aa71SJohn Baldwin struct ipmi_request *req; 546c869aa71SJohn Baldwin 547c869aa71SJohn Baldwin req = malloc(sizeof(struct ipmi_request) + requestlen + replylen, 548c869aa71SJohn Baldwin M_IPMI, M_WAITOK | M_ZERO); 549c869aa71SJohn Baldwin ipmi_init_request(req, dev, msgid, addr, command, requestlen, replylen); 550d72a0786SJohn Baldwin return (req); 55137b1ce13SDoug Ambrisko } 55237b1ce13SDoug Ambrisko 553d72a0786SJohn Baldwin /* Free a request no longer in use. */ 554d72a0786SJohn Baldwin void 555d72a0786SJohn Baldwin ipmi_free_request(struct ipmi_request *req) 556d72a0786SJohn Baldwin { 55737b1ce13SDoug Ambrisko 558d72a0786SJohn Baldwin free(req, M_IPMI); 55937b1ce13SDoug Ambrisko } 56037b1ce13SDoug Ambrisko 561d72a0786SJohn Baldwin /* Store a processed request on the appropriate completion queue. */ 562d72a0786SJohn Baldwin void 563d72a0786SJohn Baldwin ipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req) 564d72a0786SJohn Baldwin { 565d72a0786SJohn Baldwin struct ipmi_device *dev; 56637b1ce13SDoug Ambrisko 567d72a0786SJohn Baldwin IPMI_LOCK_ASSERT(sc); 568d72a0786SJohn Baldwin 569d72a0786SJohn Baldwin /* 570d72a0786SJohn Baldwin * Anonymous requests (from inside the driver) always have a 571d72a0786SJohn Baldwin * waiter that we awaken. 572d72a0786SJohn Baldwin */ 573d72a0786SJohn Baldwin if (req->ir_owner == NULL) 574d72a0786SJohn Baldwin wakeup(req); 575d72a0786SJohn Baldwin else { 576d72a0786SJohn Baldwin dev = req->ir_owner; 577d72a0786SJohn Baldwin TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link); 578d72a0786SJohn Baldwin selwakeup(&dev->ipmi_select); 579d72a0786SJohn Baldwin if (dev->ipmi_closing) 580d72a0786SJohn Baldwin wakeup(&dev->ipmi_requests); 581d72a0786SJohn Baldwin } 58237b1ce13SDoug Ambrisko } 58337b1ce13SDoug Ambrisko 584c869aa71SJohn Baldwin /* Perform an internal driver request. */ 58537b1ce13SDoug Ambrisko int 586d72a0786SJohn Baldwin ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, 587d72a0786SJohn Baldwin int timo) 588d72a0786SJohn Baldwin { 58937b1ce13SDoug Ambrisko 590c869aa71SJohn Baldwin return (sc->ipmi_driver_request(sc, req, timo)); 59137b1ce13SDoug Ambrisko } 59237b1ce13SDoug Ambrisko 593d72a0786SJohn Baldwin /* 594d72a0786SJohn Baldwin * Helper routine for polled system interfaces that use 595d72a0786SJohn Baldwin * ipmi_polled_enqueue_request() to queue requests. This request 596d72a0786SJohn Baldwin * waits until there is a pending request and then returns the first 597d72a0786SJohn Baldwin * request. If the driver is shutting down, it returns NULL. 598d72a0786SJohn Baldwin */ 599d72a0786SJohn Baldwin struct ipmi_request * 600d72a0786SJohn Baldwin ipmi_dequeue_request(struct ipmi_softc *sc) 601d72a0786SJohn Baldwin { 602d72a0786SJohn Baldwin struct ipmi_request *req; 60337b1ce13SDoug Ambrisko 604d72a0786SJohn Baldwin IPMI_LOCK_ASSERT(sc); 60537b1ce13SDoug Ambrisko 606d72a0786SJohn Baldwin while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests)) 607c869aa71SJohn Baldwin cv_wait(&sc->ipmi_request_added, &sc->ipmi_requests_lock); 608d72a0786SJohn Baldwin if (sc->ipmi_detaching) 609d72a0786SJohn Baldwin return (NULL); 61037b1ce13SDoug Ambrisko 611d72a0786SJohn Baldwin req = TAILQ_FIRST(&sc->ipmi_pending_requests); 612d72a0786SJohn Baldwin TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); 613d72a0786SJohn Baldwin return (req); 61437b1ce13SDoug Ambrisko } 61537b1ce13SDoug Ambrisko 616d72a0786SJohn Baldwin /* Default implementation of ipmi_enqueue_request() for polled interfaces. */ 617d72a0786SJohn Baldwin int 618d72a0786SJohn Baldwin ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req) 619d72a0786SJohn Baldwin { 62037b1ce13SDoug Ambrisko 6215283d39bSJohn Baldwin IPMI_LOCK_ASSERT(sc); 6225283d39bSJohn Baldwin 623d72a0786SJohn Baldwin TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link); 624d72a0786SJohn Baldwin cv_signal(&sc->ipmi_request_added); 625d72a0786SJohn Baldwin return (0); 62637b1ce13SDoug Ambrisko } 62737b1ce13SDoug Ambrisko 62837b1ce13SDoug Ambrisko /* 62937b1ce13SDoug Ambrisko * Watchdog event handler. 63037b1ce13SDoug Ambrisko */ 63137b1ce13SDoug Ambrisko 632a46a1e76SRuslan Ermilov static int 633ea2ef993SAlexander Motin ipmi_reset_watchdog(struct ipmi_softc *sc) 634ea2ef993SAlexander Motin { 635ea2ef993SAlexander Motin struct ipmi_request *req; 636ea2ef993SAlexander Motin int error; 637ea2ef993SAlexander Motin 638ea2ef993SAlexander Motin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 639ea2ef993SAlexander Motin IPMI_RESET_WDOG, 0, 0); 640ea2ef993SAlexander Motin error = ipmi_submit_driver_request(sc, req, 0); 641*9d3b47abSAlexander Motin if (error) { 642ea2ef993SAlexander Motin device_printf(sc->ipmi_dev, "Failed to reset watchdog\n"); 643*9d3b47abSAlexander Motin } else if (req->ir_compcode == 0x80) { 644*9d3b47abSAlexander Motin error = ENOENT; 645*9d3b47abSAlexander Motin } else if (req->ir_compcode != 0) { 646*9d3b47abSAlexander Motin device_printf(sc->ipmi_dev, "Watchdog reset returned 0x%x\n", 647*9d3b47abSAlexander Motin req->ir_compcode); 648*9d3b47abSAlexander Motin error = EINVAL; 649*9d3b47abSAlexander Motin } 650ea2ef993SAlexander Motin return (error); 651ea2ef993SAlexander Motin } 652ea2ef993SAlexander Motin 653ea2ef993SAlexander Motin static int 654a46a1e76SRuslan Ermilov ipmi_set_watchdog(struct ipmi_softc *sc, unsigned int sec) 655d72a0786SJohn Baldwin { 656d72a0786SJohn Baldwin struct ipmi_request *req; 657d72a0786SJohn Baldwin int error; 65837b1ce13SDoug Ambrisko 659a46a1e76SRuslan Ermilov if (sec > 0xffff / 10) 660a46a1e76SRuslan Ermilov return (EINVAL); 661a46a1e76SRuslan Ermilov 662c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 663d72a0786SJohn Baldwin IPMI_SET_WDOG, 6, 0); 66437b1ce13SDoug Ambrisko if (sec) { 665d72a0786SJohn Baldwin req->ir_request[0] = IPMI_SET_WD_TIMER_DONT_STOP 66637b1ce13SDoug Ambrisko | IPMI_SET_WD_TIMER_SMS_OS; 66714d00450SWarner Losh req->ir_request[1] = (wd_timer_actions & 0xff); 66814d00450SWarner Losh req->ir_request[2] = (wd_pretimeout_countdown & 0xff); 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); 681*9d3b47abSAlexander Motin if (error) { 682d72a0786SJohn Baldwin device_printf(sc->ipmi_dev, "Failed to set watchdog\n"); 683*9d3b47abSAlexander Motin } else if (req->ir_compcode != 0) { 684*9d3b47abSAlexander Motin device_printf(sc->ipmi_dev, "Watchdog set returned 0x%x\n", 685*9d3b47abSAlexander Motin req->ir_compcode); 686*9d3b47abSAlexander Motin error = EINVAL; 687*9d3b47abSAlexander 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 75614d00450SWarner Losh ipmi_shutdown_event(void *arg, unsigned int cmd, int *error) 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 /* 82016f0063eSWarner Losh * BMCs are notoriously slow, give it cyle_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"); 843d72a0786SJohn Baldwin TAILQ_INIT(&sc->ipmi_pending_requests); 844d72a0786SJohn Baldwin 845d72a0786SJohn Baldwin /* Initialize interface-dependent state. */ 846d72a0786SJohn Baldwin error = sc->ipmi_startup(sc); 847d72a0786SJohn Baldwin if (error) { 848d72a0786SJohn Baldwin device_printf(dev, "Failed to initialize interface: %d\n", 849d72a0786SJohn Baldwin error); 850d72a0786SJohn Baldwin return; 851d72a0786SJohn Baldwin } 852d72a0786SJohn Baldwin 853d72a0786SJohn Baldwin /* Send a GET_DEVICE_ID request. */ 854c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 855d72a0786SJohn Baldwin IPMI_GET_DEVICE_ID, 0, 15); 856d72a0786SJohn Baldwin 857d72a0786SJohn Baldwin error = ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); 858d72a0786SJohn Baldwin if (error == EWOULDBLOCK) { 859d72a0786SJohn Baldwin device_printf(dev, "Timed out waiting for GET_DEVICE_ID\n"); 860d72a0786SJohn Baldwin return; 861d72a0786SJohn Baldwin } else if (error) { 862d72a0786SJohn Baldwin device_printf(dev, "Failed GET_DEVICE_ID: %d\n", error); 863d72a0786SJohn Baldwin return; 864d72a0786SJohn Baldwin } else if (req->ir_compcode != 0) { 865d72a0786SJohn Baldwin device_printf(dev, 866d72a0786SJohn Baldwin "Bad completion code for GET_DEVICE_ID: %d\n", 867d72a0786SJohn Baldwin req->ir_compcode); 868d72a0786SJohn Baldwin return; 869d72a0786SJohn Baldwin } else if (req->ir_replylen < 5) { 870d72a0786SJohn Baldwin device_printf(dev, "Short reply for GET_DEVICE_ID: %d\n", 871d72a0786SJohn Baldwin req->ir_replylen); 872d72a0786SJohn Baldwin return; 873d72a0786SJohn Baldwin } 874d72a0786SJohn Baldwin 87514689886SRuslan Ermilov device_printf(dev, "IPMI device rev. %d, firmware rev. %d.%d%d, " 8761170c2feSWarner Losh "version %d.%d, device support mask %#x\n", 877d72a0786SJohn Baldwin req->ir_reply[1] & 0x0f, 87814689886SRuslan Ermilov req->ir_reply[2] & 0x7f, req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f, 8791170c2feSWarner Losh req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4, req->ir_reply[5]); 8801170c2feSWarner Losh 8811170c2feSWarner Losh sc->ipmi_dev_support = req->ir_reply[5]; 882d72a0786SJohn Baldwin 883c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 884d72a0786SJohn Baldwin IPMI_CLEAR_FLAGS, 1, 0); 885d72a0786SJohn Baldwin 886d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req, 0); 887d72a0786SJohn Baldwin 888d72a0786SJohn Baldwin /* XXX: Magic numbers */ 889d72a0786SJohn Baldwin if (req->ir_compcode == 0xc0) { 890d72a0786SJohn Baldwin device_printf(dev, "Clear flags is busy\n"); 891d72a0786SJohn Baldwin } 892d72a0786SJohn Baldwin if (req->ir_compcode == 0xc1) { 893d72a0786SJohn Baldwin device_printf(dev, "Clear flags illegal\n"); 894d72a0786SJohn Baldwin } 895d72a0786SJohn Baldwin 896d72a0786SJohn Baldwin for (i = 0; i < 8; i++) { 897c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 898d72a0786SJohn Baldwin IPMI_GET_CHANNEL_INFO, 1, 0); 899d72a0786SJohn Baldwin req->ir_request[0] = i; 900d72a0786SJohn Baldwin 901*9d3b47abSAlexander Motin error = ipmi_submit_driver_request(sc, req, 0); 902d72a0786SJohn Baldwin 903*9d3b47abSAlexander Motin if (error != 0 || req->ir_compcode != 0) 904d72a0786SJohn Baldwin break; 905d72a0786SJohn Baldwin } 906d72a0786SJohn Baldwin device_printf(dev, "Number of channels %d\n", i); 907d72a0786SJohn Baldwin 9089662eef5SJohn Baldwin /* 9099662eef5SJohn Baldwin * Probe for watchdog, but only for backends which support 9109662eef5SJohn Baldwin * polled driver requests. 9119662eef5SJohn Baldwin */ 9129662eef5SJohn Baldwin if (sc->ipmi_driver_requests_polled) { 913c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 914d72a0786SJohn Baldwin IPMI_GET_WDOG, 0, 0); 915d72a0786SJohn Baldwin 916*9d3b47abSAlexander Motin error = ipmi_submit_driver_request(sc, req, 0); 917d72a0786SJohn Baldwin 918*9d3b47abSAlexander Motin if (error == 0 && req->ir_compcode == 0x00) { 919d72a0786SJohn Baldwin device_printf(dev, "Attached watchdog\n"); 920d72a0786SJohn Baldwin /* register the watchdog event handler */ 9219662eef5SJohn Baldwin sc->ipmi_watchdog_tag = EVENTHANDLER_REGISTER( 9229662eef5SJohn Baldwin watchdog_list, ipmi_wd_event, sc, 0); 92314d00450SWarner Losh sc->ipmi_shutdown_tag = EVENTHANDLER_REGISTER( 92414d00450SWarner Losh shutdown_pre_sync, ipmi_shutdown_event, 92514d00450SWarner Losh sc, 0); 9269662eef5SJohn Baldwin } 927d72a0786SJohn Baldwin } 928d72a0786SJohn Baldwin 929943bebd2SJohn Baldwin sc->ipmi_cdev = make_dev(&ipmi_cdevsw, device_get_unit(dev), 930d72a0786SJohn Baldwin UID_ROOT, GID_OPERATOR, 0660, "ipmi%d", device_get_unit(dev)); 931943bebd2SJohn Baldwin if (sc->ipmi_cdev == NULL) { 932d72a0786SJohn Baldwin device_printf(dev, "Failed to create cdev\n"); 933d72a0786SJohn Baldwin return; 934d72a0786SJohn Baldwin } 935943bebd2SJohn Baldwin sc->ipmi_cdev->si_drv1 = sc; 9361170c2feSWarner Losh 9371170c2feSWarner Losh /* 93814d00450SWarner Losh * Set initial watchdog state. If desired, set an initial 93914d00450SWarner Losh * watchdog on startup. Or, if the watchdog device is 94014d00450SWarner Losh * disabled, clear any existing watchdog. 94114d00450SWarner Losh */ 94214d00450SWarner Losh if (on && wd_startup_countdown > 0) { 94314d00450SWarner Losh wd_timer_actions = IPMI_SET_WD_ACTION_POWER_CYCLE; 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 devclass_t ipmi_devclass; 1061d72a0786SJohn Baldwin 1062d72a0786SJohn Baldwin /* XXX: Why? */ 1063d72a0786SJohn Baldwin static void 1064d72a0786SJohn Baldwin ipmi_unload(void *arg) 1065d72a0786SJohn Baldwin { 1066d72a0786SJohn Baldwin device_t * devs; 1067d72a0786SJohn Baldwin int count; 1068d72a0786SJohn Baldwin int i; 1069d72a0786SJohn Baldwin 10703991dbf3SDoug Ambrisko if (ipmi_devclass == NULL) 10713991dbf3SDoug Ambrisko return; 1072bec0c98eSJohn Baldwin if (devclass_get_devices(ipmi_devclass, &devs, &count) != 0) 1073bec0c98eSJohn Baldwin return; 1074d72a0786SJohn Baldwin for (i = 0; i < count; i++) 1075d72a0786SJohn Baldwin device_delete_child(device_get_parent(devs[i]), devs[i]); 1076bec0c98eSJohn Baldwin free(devs, M_TEMP); 1077d72a0786SJohn Baldwin } 1078d72a0786SJohn Baldwin SYSUNINIT(ipmi_unload, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipmi_unload, NULL); 1079d72a0786SJohn Baldwin 108019d61f3fSDoug Ambrisko #ifdef IMPI_DEBUG 108137b1ce13SDoug Ambrisko static void 1082d72a0786SJohn Baldwin dump_buf(u_char *data, int len) 1083d72a0786SJohn Baldwin { 108437b1ce13SDoug Ambrisko char buf[20]; 108537b1ce13SDoug Ambrisko char line[1024]; 108637b1ce13SDoug Ambrisko char temp[30]; 108737b1ce13SDoug Ambrisko int count = 0; 108837b1ce13SDoug Ambrisko int i=0; 108937b1ce13SDoug Ambrisko 109037b1ce13SDoug Ambrisko printf("Address %p len %d\n", data, len); 109137b1ce13SDoug Ambrisko if (len > 256) 109237b1ce13SDoug Ambrisko len = 256; 109337b1ce13SDoug Ambrisko line[0] = '\000'; 109437b1ce13SDoug Ambrisko for (; len > 0; len--, data++) { 109537b1ce13SDoug Ambrisko sprintf(temp, "%02x ", *data); 109637b1ce13SDoug Ambrisko strcat(line, temp); 109737b1ce13SDoug Ambrisko if (*data >= ' ' && *data <= '~') 109837b1ce13SDoug Ambrisko buf[count] = *data; 109937b1ce13SDoug Ambrisko else if (*data >= 'A' && *data <= 'Z') 110037b1ce13SDoug Ambrisko buf[count] = *data; 110137b1ce13SDoug Ambrisko else 110237b1ce13SDoug Ambrisko buf[count] = '.'; 110337b1ce13SDoug Ambrisko if (++count == 16) { 110437b1ce13SDoug Ambrisko buf[count] = '\000'; 110537b1ce13SDoug Ambrisko count = 0; 110637b1ce13SDoug Ambrisko printf(" %3x %s %s\n", i, line, buf); 110737b1ce13SDoug Ambrisko i+=16; 110837b1ce13SDoug Ambrisko line[0] = '\000'; 110937b1ce13SDoug Ambrisko } 111037b1ce13SDoug Ambrisko } 111137b1ce13SDoug Ambrisko buf[count] = '\000'; 111237b1ce13SDoug Ambrisko 111337b1ce13SDoug Ambrisko for (; count != 16; count++) { 111437b1ce13SDoug Ambrisko strcat(line, " "); 111537b1ce13SDoug Ambrisko } 111637b1ce13SDoug Ambrisko printf(" %3x %s %s\n", i, line, buf); 111737b1ce13SDoug Ambrisko } 111837b1ce13SDoug Ambrisko #endif 1119