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 */ 99e3500c60SWojciech Macek static int wd_init_enable = 1; 10014d00450SWarner Losh 1017029da5cSPawel Biernacki static SYSCTL_NODE(_hw, OID_AUTO, ipmi, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 1026472ac3dSEd Schouten "IPMI driver parameters"); 10314d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RWTUN, 10437b1ce13SDoug Ambrisko &on, 0, ""); 105e3500c60SWojciech Macek SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_init_enable, CTLFLAG_RWTUN, 106e3500c60SWojciech Macek &wd_init_enable, 1, "Enable watchdog initialization"); 107016d1822SAlexander Motin SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_timer_actions, CTLFLAG_RWTUN, 10814d00450SWarner Losh &wd_timer_actions, 0, 10914d00450SWarner Losh "IPMI watchdog timer actions (including pre-timeout interrupt)"); 110016d1822SAlexander Motin SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_shutdown_countdown, CTLFLAG_RWTUN, 11114d00450SWarner Losh &wd_shutdown_countdown, 0, 11214d00450SWarner Losh "IPMI watchdog countdown for shutdown (seconds)"); 11314d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_startup_countdown, CTLFLAG_RDTUN, 11414d00450SWarner Losh &wd_startup_countdown, 0, 11514d00450SWarner Losh "IPMI watchdog countdown initialized during startup (seconds)"); 116016d1822SAlexander Motin SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_pretimeout_countdown, CTLFLAG_RWTUN, 11714d00450SWarner Losh &wd_pretimeout_countdown, 0, 11814d00450SWarner Losh "IPMI watchdog pre-timeout countdown (seconds)"); 11916f0063eSWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, cyle_wait, CTLFLAG_RWTUN, 12016f0063eSWarner Losh &cycle_wait, 0, 12116f0063eSWarner Losh "IPMI power cycle on reboot delay time (seconds)"); 12237b1ce13SDoug Ambrisko 12337b1ce13SDoug Ambrisko static struct cdevsw ipmi_cdevsw = { 12437b1ce13SDoug Ambrisko .d_version = D_VERSION, 12537b1ce13SDoug Ambrisko .d_open = ipmi_open, 12637b1ce13SDoug Ambrisko .d_ioctl = ipmi_ioctl, 12737b1ce13SDoug Ambrisko .d_poll = ipmi_poll, 12837b1ce13SDoug Ambrisko .d_name = "ipmi", 12937b1ce13SDoug Ambrisko }; 13037b1ce13SDoug Ambrisko 131d745c852SEd Schouten static MALLOC_DEFINE(M_IPMI, "ipmi", "ipmi"); 13237b1ce13SDoug Ambrisko 13337b1ce13SDoug Ambrisko static int 134d72a0786SJohn Baldwin ipmi_open(struct cdev *cdev, int flags, int fmt, struct thread *td) 13537b1ce13SDoug Ambrisko { 136d72a0786SJohn Baldwin struct ipmi_device *dev; 13737b1ce13SDoug Ambrisko struct ipmi_softc *sc; 138943bebd2SJohn Baldwin int error; 13937b1ce13SDoug Ambrisko 14037b1ce13SDoug Ambrisko if (!on) 141d72a0786SJohn Baldwin return (ENOENT); 14237b1ce13SDoug Ambrisko 143943bebd2SJohn Baldwin /* Initialize the per file descriptor data. */ 144943bebd2SJohn Baldwin dev = malloc(sizeof(struct ipmi_device), M_IPMI, M_WAITOK | M_ZERO); 145943bebd2SJohn Baldwin error = devfs_set_cdevpriv(dev, ipmi_dtor); 146943bebd2SJohn Baldwin if (error) { 147943bebd2SJohn Baldwin free(dev, M_IPMI); 148943bebd2SJohn Baldwin return (error); 14937b1ce13SDoug Ambrisko } 150943bebd2SJohn Baldwin 151943bebd2SJohn Baldwin sc = cdev->si_drv1; 152943bebd2SJohn Baldwin TAILQ_INIT(&dev->ipmi_completed_requests); 153943bebd2SJohn Baldwin dev->ipmi_address = IPMI_BMC_SLAVE_ADDR; 154943bebd2SJohn Baldwin dev->ipmi_lun = IPMI_BMC_SMS_LUN; 155943bebd2SJohn Baldwin dev->ipmi_softc = sc; 156943bebd2SJohn Baldwin IPMI_LOCK(sc); 157943bebd2SJohn Baldwin sc->ipmi_opened++; 158d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 15937b1ce13SDoug Ambrisko 160d72a0786SJohn Baldwin return (0); 16137b1ce13SDoug Ambrisko } 16237b1ce13SDoug Ambrisko 16337b1ce13SDoug Ambrisko static int 164d72a0786SJohn Baldwin ipmi_poll(struct cdev *cdev, int poll_events, struct thread *td) 16537b1ce13SDoug Ambrisko { 166d72a0786SJohn Baldwin struct ipmi_device *dev; 16737b1ce13SDoug Ambrisko struct ipmi_softc *sc; 16837b1ce13SDoug Ambrisko int revents = 0; 16937b1ce13SDoug Ambrisko 170943bebd2SJohn Baldwin if (devfs_get_cdevpriv((void **)&dev)) 171943bebd2SJohn Baldwin return (0); 17237b1ce13SDoug Ambrisko 173943bebd2SJohn Baldwin sc = cdev->si_drv1; 174d72a0786SJohn Baldwin IPMI_LOCK(sc); 17537b1ce13SDoug Ambrisko if (poll_events & (POLLIN | POLLRDNORM)) { 176d72a0786SJohn Baldwin if (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) 17737b1ce13SDoug Ambrisko revents |= poll_events & (POLLIN | POLLRDNORM); 178d72a0786SJohn Baldwin if (dev->ipmi_requests == 0) 17937b1ce13SDoug Ambrisko revents |= POLLERR; 18037b1ce13SDoug Ambrisko } 18137b1ce13SDoug Ambrisko 18237b1ce13SDoug Ambrisko if (revents == 0) { 18337b1ce13SDoug Ambrisko if (poll_events & (POLLIN | POLLRDNORM)) 184d72a0786SJohn Baldwin selrecord(td, &dev->ipmi_select); 185d72a0786SJohn Baldwin } 186d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 187d72a0786SJohn Baldwin 188d72a0786SJohn Baldwin return (revents); 18937b1ce13SDoug Ambrisko } 19037b1ce13SDoug Ambrisko 191d72a0786SJohn Baldwin static void 192d72a0786SJohn Baldwin ipmi_purge_completed_requests(struct ipmi_device *dev) 193d72a0786SJohn Baldwin { 194d72a0786SJohn Baldwin struct ipmi_request *req; 195d72a0786SJohn Baldwin 196d72a0786SJohn Baldwin while (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) { 197d72a0786SJohn Baldwin req = TAILQ_FIRST(&dev->ipmi_completed_requests); 198d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, req, ir_link); 199d72a0786SJohn Baldwin dev->ipmi_requests--; 200d72a0786SJohn Baldwin ipmi_free_request(req); 201d72a0786SJohn Baldwin } 20237b1ce13SDoug Ambrisko } 20337b1ce13SDoug Ambrisko 204943bebd2SJohn Baldwin static void 205943bebd2SJohn Baldwin ipmi_dtor(void *arg) 20637b1ce13SDoug Ambrisko { 207d72a0786SJohn Baldwin struct ipmi_request *req, *nreq; 208d72a0786SJohn Baldwin struct ipmi_device *dev; 20937b1ce13SDoug Ambrisko struct ipmi_softc *sc; 21037b1ce13SDoug Ambrisko 211943bebd2SJohn Baldwin dev = arg; 212d72a0786SJohn Baldwin sc = dev->ipmi_softc; 21337b1ce13SDoug Ambrisko 214d72a0786SJohn Baldwin IPMI_LOCK(sc); 215d72a0786SJohn Baldwin if (dev->ipmi_requests) { 216d72a0786SJohn Baldwin /* Throw away any pending requests for this device. */ 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 24837b1ce13SDoug Ambrisko #ifdef IPMB 24937b1ce13SDoug Ambrisko static int 25037b1ce13SDoug Ambrisko ipmi_ipmb_checksum(u_char *data, int len) 25137b1ce13SDoug Ambrisko { 25237b1ce13SDoug Ambrisko u_char sum = 0; 25337b1ce13SDoug Ambrisko 25437b1ce13SDoug Ambrisko for (; len; len--) { 25537b1ce13SDoug Ambrisko sum += *data++; 25637b1ce13SDoug Ambrisko } 257d72a0786SJohn Baldwin return (-sum); 25837b1ce13SDoug Ambrisko } 25937b1ce13SDoug Ambrisko 260d72a0786SJohn Baldwin /* XXX: Needs work */ 26137b1ce13SDoug Ambrisko static int 26237b1ce13SDoug Ambrisko ipmi_ipmb_send_message(device_t dev, u_char channel, u_char netfn, 26337b1ce13SDoug Ambrisko u_char command, u_char seq, u_char *data, int data_len) 26437b1ce13SDoug Ambrisko { 26537b1ce13SDoug Ambrisko struct ipmi_softc *sc = device_get_softc(dev); 266d72a0786SJohn Baldwin struct ipmi_request *req; 26737b1ce13SDoug Ambrisko u_char slave_addr = 0x52; 268d72a0786SJohn Baldwin int error; 26937b1ce13SDoug Ambrisko 270c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 271d72a0786SJohn Baldwin IPMI_SEND_MSG, data_len + 8, 0); 272d72a0786SJohn Baldwin req->ir_request[0] = channel; 273d72a0786SJohn Baldwin req->ir_request[1] = slave_addr; 274d72a0786SJohn Baldwin req->ir_request[2] = IPMI_ADDR(netfn, 0); 275d72a0786SJohn Baldwin req->ir_request[3] = ipmi_ipmb_checksum(&req->ir_request[1], 2); 276d72a0786SJohn Baldwin req->ir_request[4] = sc->ipmi_address; 277d72a0786SJohn Baldwin req->ir_request[5] = IPMI_ADDR(seq, sc->ipmi_lun); 278d72a0786SJohn Baldwin req->ir_request[6] = command; 27937b1ce13SDoug Ambrisko 280d72a0786SJohn Baldwin bcopy(data, &req->ir_request[7], data_len); 281d72a0786SJohn Baldwin temp[data_len + 7] = ipmi_ipmb_checksum(&req->ir_request[4], 282d72a0786SJohn Baldwin data_len + 3); 28337b1ce13SDoug Ambrisko 284d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req); 285d72a0786SJohn Baldwin error = req->ir_error; 28637b1ce13SDoug Ambrisko 287d72a0786SJohn Baldwin return (error); 28837b1ce13SDoug Ambrisko } 28937b1ce13SDoug Ambrisko 29037b1ce13SDoug Ambrisko static int 291d72a0786SJohn Baldwin ipmi_handle_attn(struct ipmi_softc *sc) 29237b1ce13SDoug Ambrisko { 293d72a0786SJohn Baldwin struct ipmi_request *req; 29437b1ce13SDoug Ambrisko int error; 29537b1ce13SDoug Ambrisko 29637b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "BMC has a message\n"); 297c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 298d72a0786SJohn Baldwin IPMI_GET_MSG_FLAGS, 0, 1); 29937b1ce13SDoug Ambrisko 300d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req); 301d72a0786SJohn Baldwin 302d72a0786SJohn Baldwin if (req->ir_error == 0 && req->ir_compcode == 0) { 303d72a0786SJohn Baldwin if (req->ir_reply[0] & IPMI_MSG_BUFFER_FULL) { 30437b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "message buffer full"); 30537b1ce13SDoug Ambrisko } 306d72a0786SJohn Baldwin if (req->ir_reply[0] & IPMI_WDT_PRE_TIMEOUT) { 30737b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, 30837b1ce13SDoug Ambrisko "watchdog about to go off"); 30937b1ce13SDoug Ambrisko } 310d72a0786SJohn Baldwin if (req->ir_reply[0] & IPMI_MSG_AVAILABLE) { 311c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, 312d72a0786SJohn Baldwin IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG, 0, 313d72a0786SJohn Baldwin 16); 31437b1ce13SDoug Ambrisko 31537b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "throw out message "); 31637b1ce13SDoug Ambrisko dump_buf(temp, 16); 31737b1ce13SDoug Ambrisko } 31837b1ce13SDoug Ambrisko } 319d72a0786SJohn Baldwin error = req->ir_error; 320d72a0786SJohn Baldwin 321d72a0786SJohn Baldwin return (error); 322d72a0786SJohn Baldwin } 323d72a0786SJohn Baldwin #endif 324d72a0786SJohn Baldwin 32537b1ce13SDoug Ambrisko static int 326d72a0786SJohn Baldwin ipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, 32737b1ce13SDoug Ambrisko int flags, struct thread *td) 32837b1ce13SDoug Ambrisko { 32937b1ce13SDoug Ambrisko struct ipmi_softc *sc; 330d72a0786SJohn Baldwin struct ipmi_device *dev; 331d72a0786SJohn Baldwin struct ipmi_request *kreq; 33237b1ce13SDoug Ambrisko struct ipmi_req *req = (struct ipmi_req *)data; 33337b1ce13SDoug Ambrisko struct ipmi_recv *recv = (struct ipmi_recv *)data; 33437b1ce13SDoug Ambrisko struct ipmi_addr addr; 335d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 336d72a0786SJohn Baldwin struct ipmi_req32 *req32 = (struct ipmi_req32 *)data; 337d72a0786SJohn Baldwin struct ipmi_recv32 *recv32 = (struct ipmi_recv32 *)data; 338d72a0786SJohn Baldwin union { 339d72a0786SJohn Baldwin struct ipmi_req req; 340d72a0786SJohn Baldwin struct ipmi_recv recv; 341d72a0786SJohn Baldwin } thunk32; 342d72a0786SJohn Baldwin #endif 34337b1ce13SDoug Ambrisko int error, len; 34437b1ce13SDoug Ambrisko 345943bebd2SJohn Baldwin error = devfs_get_cdevpriv((void **)&dev); 346943bebd2SJohn Baldwin if (error) 347943bebd2SJohn Baldwin return (error); 348943bebd2SJohn Baldwin 349943bebd2SJohn Baldwin sc = cdev->si_drv1; 350d72a0786SJohn Baldwin 351d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 352d72a0786SJohn Baldwin /* Convert 32-bit structures to native. */ 353d72a0786SJohn Baldwin switch (cmd) { 354d72a0786SJohn Baldwin case IPMICTL_SEND_COMMAND_32: 355d72a0786SJohn Baldwin req = &thunk32.req; 356d72a0786SJohn Baldwin req->addr = PTRIN(req32->addr); 357d72a0786SJohn Baldwin req->addr_len = req32->addr_len; 358d72a0786SJohn Baldwin req->msgid = req32->msgid; 359d72a0786SJohn Baldwin req->msg.netfn = req32->msg.netfn; 360d72a0786SJohn Baldwin req->msg.cmd = req32->msg.cmd; 361d72a0786SJohn Baldwin req->msg.data_len = req32->msg.data_len; 362d72a0786SJohn Baldwin req->msg.data = PTRIN(req32->msg.data); 363d72a0786SJohn Baldwin break; 364d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 365d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 366d72a0786SJohn Baldwin recv = &thunk32.recv; 367d72a0786SJohn Baldwin recv->addr = PTRIN(recv32->addr); 368d72a0786SJohn Baldwin recv->addr_len = recv32->addr_len; 369d72a0786SJohn Baldwin recv->msg.data_len = recv32->msg.data_len; 370d72a0786SJohn Baldwin recv->msg.data = PTRIN(recv32->msg.data); 371d72a0786SJohn Baldwin break; 372d72a0786SJohn Baldwin } 373d72a0786SJohn Baldwin #endif 37437b1ce13SDoug Ambrisko 37537b1ce13SDoug Ambrisko switch (cmd) { 376d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 377d72a0786SJohn Baldwin case IPMICTL_SEND_COMMAND_32: 378d72a0786SJohn Baldwin #endif 37937b1ce13SDoug Ambrisko case IPMICTL_SEND_COMMAND: 380d72a0786SJohn Baldwin /* 381d72a0786SJohn Baldwin * XXX: Need to add proper handling of this. 382d72a0786SJohn Baldwin */ 38337b1ce13SDoug Ambrisko error = copyin(req->addr, &addr, sizeof(addr)); 384d72a0786SJohn Baldwin if (error) 385d72a0786SJohn Baldwin return (error); 386d72a0786SJohn Baldwin 387d72a0786SJohn Baldwin IPMI_LOCK(sc); 388d72a0786SJohn Baldwin /* clear out old stuff in queue of stuff done */ 389d72a0786SJohn Baldwin /* XXX: This seems odd. */ 390d72a0786SJohn Baldwin while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))) { 391d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, 392d72a0786SJohn Baldwin ir_link); 393d72a0786SJohn Baldwin dev->ipmi_requests--; 394d72a0786SJohn Baldwin ipmi_free_request(kreq); 39537b1ce13SDoug Ambrisko } 396d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 397d72a0786SJohn Baldwin 398d72a0786SJohn Baldwin kreq = ipmi_alloc_request(dev, req->msgid, 399d72a0786SJohn Baldwin IPMI_ADDR(req->msg.netfn, 0), req->msg.cmd, 400d72a0786SJohn Baldwin req->msg.data_len, IPMI_MAX_RX); 401d72a0786SJohn Baldwin error = copyin(req->msg.data, kreq->ir_request, 40237b1ce13SDoug Ambrisko req->msg.data_len); 403d72a0786SJohn Baldwin if (error) { 404d72a0786SJohn Baldwin ipmi_free_request(kreq); 405d72a0786SJohn Baldwin return (error); 40637b1ce13SDoug Ambrisko } 407d72a0786SJohn Baldwin IPMI_LOCK(sc); 408d72a0786SJohn Baldwin dev->ipmi_requests++; 409d72a0786SJohn Baldwin error = sc->ipmi_enqueue_request(sc, kreq); 410d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 411d72a0786SJohn Baldwin if (error) 412d72a0786SJohn Baldwin return (error); 413d72a0786SJohn Baldwin break; 414d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 415d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 416d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 417d72a0786SJohn Baldwin #endif 41837b1ce13SDoug Ambrisko case IPMICTL_RECEIVE_MSG_TRUNC: 41937b1ce13SDoug Ambrisko case IPMICTL_RECEIVE_MSG: 42037b1ce13SDoug Ambrisko error = copyin(recv->addr, &addr, sizeof(addr)); 421d72a0786SJohn Baldwin if (error) 422d72a0786SJohn Baldwin return (error); 423d72a0786SJohn Baldwin 424d72a0786SJohn Baldwin IPMI_LOCK(sc); 425d72a0786SJohn Baldwin kreq = TAILQ_FIRST(&dev->ipmi_completed_requests); 426d72a0786SJohn Baldwin if (kreq == NULL) { 427d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 428d72a0786SJohn Baldwin return (EAGAIN); 42937b1ce13SDoug Ambrisko } 430d72a0786SJohn Baldwin addr.channel = IPMI_BMC_CHANNEL; 431d72a0786SJohn Baldwin /* XXX */ 432d72a0786SJohn Baldwin recv->recv_type = IPMI_RESPONSE_RECV_TYPE; 433d72a0786SJohn Baldwin recv->msgid = kreq->ir_msgid; 434d72a0786SJohn Baldwin recv->msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2; 435d72a0786SJohn Baldwin recv->msg.cmd = kreq->ir_command; 436d72a0786SJohn Baldwin error = kreq->ir_error; 437d72a0786SJohn Baldwin if (error) { 438d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, 439d72a0786SJohn Baldwin ir_link); 440d72a0786SJohn Baldwin dev->ipmi_requests--; 441d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 442d72a0786SJohn Baldwin ipmi_free_request(kreq); 443d72a0786SJohn Baldwin return (error); 444d72a0786SJohn Baldwin } 445d72a0786SJohn Baldwin len = kreq->ir_replylen + 1; 446d72a0786SJohn Baldwin if (recv->msg.data_len < len && 447d72a0786SJohn Baldwin (cmd == IPMICTL_RECEIVE_MSG 448d72a0786SJohn Baldwin #ifdef IPMICTL_RECEIVE_MSG_32 449c12dbd1dSDavid E. O'Brien || cmd == IPMICTL_RECEIVE_MSG_32 450d72a0786SJohn Baldwin #endif 451d72a0786SJohn Baldwin )) { 452d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 453d72a0786SJohn Baldwin return (EMSGSIZE); 454d72a0786SJohn Baldwin } 455d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link); 456d72a0786SJohn Baldwin dev->ipmi_requests--; 457d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 45837b1ce13SDoug Ambrisko len = min(recv->msg.data_len, len); 45937b1ce13SDoug Ambrisko recv->msg.data_len = len; 46037b1ce13SDoug Ambrisko error = copyout(&addr, recv->addr,sizeof(addr)); 46137b1ce13SDoug Ambrisko if (error == 0) 462d72a0786SJohn Baldwin error = copyout(&kreq->ir_compcode, recv->msg.data, 1); 463d72a0786SJohn Baldwin if (error == 0) 464d72a0786SJohn Baldwin error = copyout(kreq->ir_reply, recv->msg.data + 1, 465d72a0786SJohn Baldwin len - 1); 466d72a0786SJohn Baldwin ipmi_free_request(kreq); 467d72a0786SJohn Baldwin if (error) 468d72a0786SJohn Baldwin return (error); 469d72a0786SJohn Baldwin break; 47037b1ce13SDoug Ambrisko case IPMICTL_SET_MY_ADDRESS_CMD: 471d72a0786SJohn Baldwin IPMI_LOCK(sc); 472d72a0786SJohn Baldwin dev->ipmi_address = *(int*)data; 473d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 474d72a0786SJohn Baldwin break; 47537b1ce13SDoug Ambrisko case IPMICTL_GET_MY_ADDRESS_CMD: 476d72a0786SJohn Baldwin IPMI_LOCK(sc); 477d72a0786SJohn Baldwin *(int*)data = dev->ipmi_address; 478d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 479d72a0786SJohn Baldwin break; 48037b1ce13SDoug Ambrisko case IPMICTL_SET_MY_LUN_CMD: 481d72a0786SJohn Baldwin IPMI_LOCK(sc); 482d72a0786SJohn Baldwin dev->ipmi_lun = *(int*)data & 0x3; 483d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 484d72a0786SJohn Baldwin break; 48537b1ce13SDoug Ambrisko case IPMICTL_GET_MY_LUN_CMD: 486d72a0786SJohn Baldwin IPMI_LOCK(sc); 487d72a0786SJohn Baldwin *(int*)data = dev->ipmi_lun; 488d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 489d72a0786SJohn Baldwin break; 49037b1ce13SDoug Ambrisko case IPMICTL_SET_GETS_EVENTS_CMD: 49137b1ce13SDoug Ambrisko /* 49237b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, 49337b1ce13SDoug Ambrisko "IPMICTL_SET_GETS_EVENTS_CMD NA\n"); 49437b1ce13SDoug Ambrisko */ 495d72a0786SJohn Baldwin break; 49637b1ce13SDoug Ambrisko case IPMICTL_REGISTER_FOR_CMD: 49737b1ce13SDoug Ambrisko case IPMICTL_UNREGISTER_FOR_CMD: 498d72a0786SJohn Baldwin return (EOPNOTSUPP); 499d72a0786SJohn Baldwin default: 50037b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "Unknown IOCTL %lX\n", cmd); 501d72a0786SJohn Baldwin return (ENOIOCTL); 50237b1ce13SDoug Ambrisko } 50337b1ce13SDoug Ambrisko 504d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 505d72a0786SJohn Baldwin /* Update changed fields in 32-bit structures. */ 506d72a0786SJohn Baldwin switch (cmd) { 507d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 508d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 509d72a0786SJohn Baldwin recv32->recv_type = recv->recv_type; 510d72a0786SJohn Baldwin recv32->msgid = recv->msgid; 511d72a0786SJohn Baldwin recv32->msg.netfn = recv->msg.netfn; 512d72a0786SJohn Baldwin recv32->msg.cmd = recv->msg.cmd; 513d72a0786SJohn Baldwin recv32->msg.data_len = recv->msg.data_len; 51437b1ce13SDoug Ambrisko break; 51537b1ce13SDoug Ambrisko } 516d72a0786SJohn Baldwin #endif 517d72a0786SJohn Baldwin return (0); 51837b1ce13SDoug Ambrisko } 51937b1ce13SDoug Ambrisko 520d72a0786SJohn Baldwin /* 521d72a0786SJohn Baldwin * Request management. 522d72a0786SJohn Baldwin */ 52337b1ce13SDoug Ambrisko 524c869aa71SJohn Baldwin static __inline void 525c869aa71SJohn Baldwin ipmi_init_request(struct ipmi_request *req, struct ipmi_device *dev, long msgid, 526c869aa71SJohn Baldwin uint8_t addr, uint8_t command, size_t requestlen, size_t replylen) 527d72a0786SJohn Baldwin { 528d72a0786SJohn Baldwin 529d72a0786SJohn Baldwin req->ir_owner = dev; 530d72a0786SJohn Baldwin req->ir_msgid = msgid; 531d72a0786SJohn Baldwin req->ir_addr = addr; 532d72a0786SJohn Baldwin req->ir_command = command; 533d72a0786SJohn Baldwin if (requestlen) { 534d72a0786SJohn Baldwin req->ir_request = (char *)&req[1]; 535d72a0786SJohn Baldwin req->ir_requestlen = requestlen; 536d72a0786SJohn Baldwin } 537d72a0786SJohn Baldwin if (replylen) { 538d72a0786SJohn Baldwin req->ir_reply = (char *)&req[1] + requestlen; 539d72a0786SJohn Baldwin req->ir_replybuflen = replylen; 540d72a0786SJohn Baldwin } 541c869aa71SJohn Baldwin } 542c869aa71SJohn Baldwin 543c869aa71SJohn Baldwin /* Allocate a new request with request and reply buffers. */ 544c869aa71SJohn Baldwin struct ipmi_request * 545c869aa71SJohn Baldwin ipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr, 546c869aa71SJohn Baldwin uint8_t command, size_t requestlen, size_t replylen) 547c869aa71SJohn Baldwin { 548c869aa71SJohn Baldwin struct ipmi_request *req; 549c869aa71SJohn Baldwin 550c869aa71SJohn Baldwin req = malloc(sizeof(struct ipmi_request) + requestlen + replylen, 551c869aa71SJohn Baldwin M_IPMI, M_WAITOK | M_ZERO); 552c869aa71SJohn Baldwin ipmi_init_request(req, dev, msgid, addr, command, requestlen, replylen); 553d72a0786SJohn Baldwin return (req); 55437b1ce13SDoug Ambrisko } 55537b1ce13SDoug Ambrisko 556d72a0786SJohn Baldwin /* Free a request no longer in use. */ 557d72a0786SJohn Baldwin void 558d72a0786SJohn Baldwin ipmi_free_request(struct ipmi_request *req) 559d72a0786SJohn Baldwin { 56037b1ce13SDoug Ambrisko 561d72a0786SJohn Baldwin free(req, M_IPMI); 56237b1ce13SDoug Ambrisko } 56337b1ce13SDoug Ambrisko 564d72a0786SJohn Baldwin /* Store a processed request on the appropriate completion queue. */ 565d72a0786SJohn Baldwin void 566d72a0786SJohn Baldwin ipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req) 567d72a0786SJohn Baldwin { 568d72a0786SJohn Baldwin struct ipmi_device *dev; 56937b1ce13SDoug Ambrisko 570d72a0786SJohn Baldwin IPMI_LOCK_ASSERT(sc); 571d72a0786SJohn Baldwin 572d72a0786SJohn Baldwin /* 573d72a0786SJohn Baldwin * Anonymous requests (from inside the driver) always have a 574d72a0786SJohn Baldwin * waiter that we awaken. 575d72a0786SJohn Baldwin */ 576d72a0786SJohn Baldwin if (req->ir_owner == NULL) 577d72a0786SJohn Baldwin wakeup(req); 578d72a0786SJohn Baldwin else { 579d72a0786SJohn Baldwin dev = req->ir_owner; 580d72a0786SJohn Baldwin TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link); 581d72a0786SJohn Baldwin selwakeup(&dev->ipmi_select); 582d72a0786SJohn Baldwin if (dev->ipmi_closing) 583d72a0786SJohn Baldwin wakeup(&dev->ipmi_requests); 584d72a0786SJohn Baldwin } 58537b1ce13SDoug Ambrisko } 58637b1ce13SDoug Ambrisko 587c869aa71SJohn Baldwin /* Perform an internal driver request. */ 58837b1ce13SDoug Ambrisko int 589d72a0786SJohn Baldwin ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, 590d72a0786SJohn Baldwin int timo) 591d72a0786SJohn Baldwin { 59237b1ce13SDoug Ambrisko 593c869aa71SJohn Baldwin return (sc->ipmi_driver_request(sc, req, timo)); 59437b1ce13SDoug Ambrisko } 59537b1ce13SDoug Ambrisko 596d72a0786SJohn Baldwin /* 597d72a0786SJohn Baldwin * Helper routine for polled system interfaces that use 598d72a0786SJohn Baldwin * ipmi_polled_enqueue_request() to queue requests. This request 599d72a0786SJohn Baldwin * waits until there is a pending request and then returns the first 600d72a0786SJohn Baldwin * request. If the driver is shutting down, it returns NULL. 601d72a0786SJohn Baldwin */ 602d72a0786SJohn Baldwin struct ipmi_request * 603d72a0786SJohn Baldwin ipmi_dequeue_request(struct ipmi_softc *sc) 604d72a0786SJohn Baldwin { 605d72a0786SJohn Baldwin struct ipmi_request *req; 60637b1ce13SDoug Ambrisko 607d72a0786SJohn Baldwin IPMI_LOCK_ASSERT(sc); 60837b1ce13SDoug Ambrisko 609d72a0786SJohn Baldwin while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests)) 610c869aa71SJohn Baldwin cv_wait(&sc->ipmi_request_added, &sc->ipmi_requests_lock); 611d72a0786SJohn Baldwin if (sc->ipmi_detaching) 612d72a0786SJohn Baldwin return (NULL); 61337b1ce13SDoug Ambrisko 614d72a0786SJohn Baldwin req = TAILQ_FIRST(&sc->ipmi_pending_requests); 615d72a0786SJohn Baldwin TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); 616d72a0786SJohn Baldwin return (req); 61737b1ce13SDoug Ambrisko } 61837b1ce13SDoug Ambrisko 619d72a0786SJohn Baldwin /* Default implementation of ipmi_enqueue_request() for polled interfaces. */ 620d72a0786SJohn Baldwin int 621d72a0786SJohn Baldwin ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req) 622d72a0786SJohn Baldwin { 62337b1ce13SDoug Ambrisko 6245283d39bSJohn Baldwin IPMI_LOCK_ASSERT(sc); 6255283d39bSJohn Baldwin 626d72a0786SJohn Baldwin TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link); 627d72a0786SJohn Baldwin cv_signal(&sc->ipmi_request_added); 628d72a0786SJohn Baldwin return (0); 62937b1ce13SDoug Ambrisko } 63037b1ce13SDoug Ambrisko 63137b1ce13SDoug Ambrisko /* 63237b1ce13SDoug Ambrisko * Watchdog event handler. 63337b1ce13SDoug Ambrisko */ 63437b1ce13SDoug Ambrisko 635a46a1e76SRuslan Ermilov static int 636ea2ef993SAlexander Motin ipmi_reset_watchdog(struct ipmi_softc *sc) 637ea2ef993SAlexander Motin { 638ea2ef993SAlexander Motin struct ipmi_request *req; 639ea2ef993SAlexander Motin int error; 640ea2ef993SAlexander Motin 641ea2ef993SAlexander Motin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 642ea2ef993SAlexander Motin IPMI_RESET_WDOG, 0, 0); 643ea2ef993SAlexander Motin error = ipmi_submit_driver_request(sc, req, 0); 6449d3b47abSAlexander Motin if (error) { 645ea2ef993SAlexander Motin device_printf(sc->ipmi_dev, "Failed to reset watchdog\n"); 6469d3b47abSAlexander Motin } else if (req->ir_compcode == 0x80) { 6479d3b47abSAlexander Motin error = ENOENT; 6489d3b47abSAlexander Motin } else if (req->ir_compcode != 0) { 6499d3b47abSAlexander Motin device_printf(sc->ipmi_dev, "Watchdog reset returned 0x%x\n", 6509d3b47abSAlexander Motin req->ir_compcode); 6519d3b47abSAlexander Motin error = EINVAL; 6529d3b47abSAlexander Motin } 653ea2ef993SAlexander Motin return (error); 654ea2ef993SAlexander Motin } 655ea2ef993SAlexander Motin 656ea2ef993SAlexander Motin static int 657a46a1e76SRuslan Ermilov ipmi_set_watchdog(struct ipmi_softc *sc, unsigned int sec) 658d72a0786SJohn Baldwin { 659d72a0786SJohn Baldwin struct ipmi_request *req; 660d72a0786SJohn Baldwin int error; 66137b1ce13SDoug Ambrisko 662a46a1e76SRuslan Ermilov if (sec > 0xffff / 10) 663a46a1e76SRuslan Ermilov return (EINVAL); 664a46a1e76SRuslan Ermilov 665c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 666d72a0786SJohn Baldwin IPMI_SET_WDOG, 6, 0); 66737b1ce13SDoug Ambrisko if (sec) { 668d72a0786SJohn Baldwin req->ir_request[0] = IPMI_SET_WD_TIMER_DONT_STOP 66937b1ce13SDoug Ambrisko | IPMI_SET_WD_TIMER_SMS_OS; 67014d00450SWarner Losh req->ir_request[1] = (wd_timer_actions & 0xff); 6716c2d4404SAlexander Motin req->ir_request[2] = min(0xff, 6726c2d4404SAlexander Motin min(wd_pretimeout_countdown, (sec + 2) / 4)); 673d72a0786SJohn Baldwin req->ir_request[3] = 0; /* Timer use */ 674d72a0786SJohn Baldwin req->ir_request[4] = (sec * 10) & 0xff; 675a46a1e76SRuslan Ermilov req->ir_request[5] = (sec * 10) >> 8; 67637b1ce13SDoug Ambrisko } else { 677d72a0786SJohn Baldwin req->ir_request[0] = IPMI_SET_WD_TIMER_SMS_OS; 678d72a0786SJohn Baldwin req->ir_request[1] = 0; 679d72a0786SJohn Baldwin req->ir_request[2] = 0; 680d72a0786SJohn Baldwin req->ir_request[3] = 0; /* Timer use */ 681d72a0786SJohn Baldwin req->ir_request[4] = 0; 682d72a0786SJohn Baldwin req->ir_request[5] = 0; 68337b1ce13SDoug Ambrisko } 684d72a0786SJohn Baldwin error = ipmi_submit_driver_request(sc, req, 0); 6859d3b47abSAlexander Motin if (error) { 686d72a0786SJohn Baldwin device_printf(sc->ipmi_dev, "Failed to set watchdog\n"); 6879d3b47abSAlexander Motin } else if (req->ir_compcode != 0) { 6889d3b47abSAlexander Motin device_printf(sc->ipmi_dev, "Watchdog set returned 0x%x\n", 6899d3b47abSAlexander Motin req->ir_compcode); 6909d3b47abSAlexander Motin error = EINVAL; 6919d3b47abSAlexander Motin } 692a46a1e76SRuslan Ermilov return (error); 69337b1ce13SDoug Ambrisko } 69437b1ce13SDoug Ambrisko 69537b1ce13SDoug Ambrisko static void 69637b1ce13SDoug Ambrisko ipmi_wd_event(void *arg, unsigned int cmd, int *error) 69737b1ce13SDoug Ambrisko { 69837b1ce13SDoug Ambrisko struct ipmi_softc *sc = arg; 69937b1ce13SDoug Ambrisko unsigned int timeout; 700a46a1e76SRuslan Ermilov int e; 70137b1ce13SDoug Ambrisko 70214d00450SWarner Losh /* Ignore requests while disabled. */ 70314d00450SWarner Losh if (!on) 704a9b3c1bfSGleb Smirnoff return; 705a9b3c1bfSGleb Smirnoff 70614d00450SWarner Losh /* 70714d00450SWarner Losh * To prevent infinite hangs, we don't let anyone pat or change 70814d00450SWarner Losh * the watchdog when we're shutting down. (See ipmi_shutdown_event().) 70914d00450SWarner Losh * However, we do want to keep patting the watchdog while we are doing 71014d00450SWarner Losh * a coredump. 71114d00450SWarner Losh */ 71214d00450SWarner Losh if (wd_in_shutdown) { 71314d00450SWarner Losh if (dumping && sc->ipmi_watchdog_active) 71414d00450SWarner Losh ipmi_reset_watchdog(sc); 71514d00450SWarner Losh return; 71614d00450SWarner Losh } 71714d00450SWarner Losh 71837b1ce13SDoug Ambrisko cmd &= WD_INTERVAL; 7199079fff5SNick Hibma if (cmd > 0 && cmd <= 63) { 720a46a1e76SRuslan Ermilov timeout = ((uint64_t)1 << cmd) / 1000000000; 721a46a1e76SRuslan Ermilov if (timeout == 0) 722a46a1e76SRuslan Ermilov timeout = 1; 72314d00450SWarner Losh if (timeout != sc->ipmi_watchdog_active || 72414d00450SWarner Losh wd_timer_actions != sc->ipmi_watchdog_actions || 72514d00450SWarner Losh wd_pretimeout_countdown != sc->ipmi_watchdog_pretimeout) { 726a46a1e76SRuslan Ermilov e = ipmi_set_watchdog(sc, timeout); 7271710852eSJohn Baldwin if (e == 0) { 728ea2ef993SAlexander Motin sc->ipmi_watchdog_active = timeout; 72914d00450SWarner Losh sc->ipmi_watchdog_actions = wd_timer_actions; 73014d00450SWarner Losh sc->ipmi_watchdog_pretimeout = wd_pretimeout_countdown; 731ea2ef993SAlexander Motin } else { 732a46a1e76SRuslan Ermilov (void)ipmi_set_watchdog(sc, 0); 733ea2ef993SAlexander Motin sc->ipmi_watchdog_active = 0; 73414d00450SWarner Losh sc->ipmi_watchdog_actions = 0; 73514d00450SWarner Losh sc->ipmi_watchdog_pretimeout = 0; 736ea2ef993SAlexander Motin } 737ea2ef993SAlexander Motin } 738ea2ef993SAlexander Motin if (sc->ipmi_watchdog_active != 0) { 739ea2ef993SAlexander Motin e = ipmi_reset_watchdog(sc); 740ea2ef993SAlexander Motin if (e == 0) { 741ea2ef993SAlexander Motin *error = 0; 742ea2ef993SAlexander Motin } else { 743ea2ef993SAlexander Motin (void)ipmi_set_watchdog(sc, 0); 744ea2ef993SAlexander Motin sc->ipmi_watchdog_active = 0; 74514d00450SWarner Losh sc->ipmi_watchdog_actions = 0; 74614d00450SWarner Losh sc->ipmi_watchdog_pretimeout = 0; 747ea2ef993SAlexander Motin } 748ea2ef993SAlexander Motin } 7491710852eSJohn Baldwin } else if (atomic_readandclear_int(&sc->ipmi_watchdog_active) != 0) { 75014d00450SWarner Losh sc->ipmi_watchdog_actions = 0; 75114d00450SWarner Losh sc->ipmi_watchdog_pretimeout = 0; 75214d00450SWarner Losh 753a46a1e76SRuslan Ermilov e = ipmi_set_watchdog(sc, 0); 754a46a1e76SRuslan Ermilov if (e != 0 && cmd == 0) 755a46a1e76SRuslan Ermilov *error = EOPNOTSUPP; 7569079fff5SNick Hibma } 75737b1ce13SDoug Ambrisko } 75837b1ce13SDoug Ambrisko 759d72a0786SJohn Baldwin static void 76014d00450SWarner Losh ipmi_shutdown_event(void *arg, unsigned int cmd, int *error) 76114d00450SWarner Losh { 76214d00450SWarner Losh struct ipmi_softc *sc = arg; 76314d00450SWarner Losh 76414d00450SWarner Losh /* Ignore event if disabled. */ 76514d00450SWarner Losh if (!on) 76614d00450SWarner Losh return; 76714d00450SWarner Losh 76814d00450SWarner Losh /* 76914d00450SWarner Losh * Positive wd_shutdown_countdown value will re-arm watchdog; 77014d00450SWarner Losh * Zero value in wd_shutdown_countdown will disable watchdog; 77114d00450SWarner Losh * Negative value in wd_shutdown_countdown will keep existing state; 77214d00450SWarner Losh * 77314d00450SWarner Losh * Revert to using a power cycle to ensure that the watchdog will 77414d00450SWarner Losh * do something useful here. Having the watchdog send an NMI 77514d00450SWarner Losh * instead is useless during shutdown, and might be ignored if an 77614d00450SWarner Losh * NMI already triggered. 77714d00450SWarner Losh */ 77814d00450SWarner Losh 77914d00450SWarner Losh wd_in_shutdown = true; 78014d00450SWarner Losh if (wd_shutdown_countdown == 0) { 78114d00450SWarner Losh /* disable watchdog */ 78214d00450SWarner Losh ipmi_set_watchdog(sc, 0); 78314d00450SWarner Losh sc->ipmi_watchdog_active = 0; 78414d00450SWarner Losh } else if (wd_shutdown_countdown > 0) { 78514d00450SWarner Losh /* set desired action and time, and, reset watchdog */ 78614d00450SWarner Losh wd_timer_actions = IPMI_SET_WD_ACTION_POWER_CYCLE; 78714d00450SWarner Losh ipmi_set_watchdog(sc, wd_shutdown_countdown); 78814d00450SWarner Losh sc->ipmi_watchdog_active = wd_shutdown_countdown; 78914d00450SWarner Losh ipmi_reset_watchdog(sc); 79014d00450SWarner Losh } 79114d00450SWarner Losh } 79214d00450SWarner Losh 79314d00450SWarner Losh static void 7941170c2feSWarner Losh ipmi_power_cycle(void *arg, int howto) 7951170c2feSWarner Losh { 7961170c2feSWarner Losh struct ipmi_softc *sc = arg; 7971170c2feSWarner Losh struct ipmi_request *req; 7981170c2feSWarner Losh 7991170c2feSWarner Losh /* 8001170c2feSWarner Losh * Ignore everything except power cycling requests 8011170c2feSWarner Losh */ 8021170c2feSWarner Losh if ((howto & RB_POWERCYCLE) == 0) 8031170c2feSWarner Losh return; 8041170c2feSWarner Losh 8051170c2feSWarner Losh device_printf(sc->ipmi_dev, "Power cycling using IPMI\n"); 8061170c2feSWarner Losh 8071170c2feSWarner Losh /* 8081170c2feSWarner Losh * Send a CHASSIS_CONTROL command to the CHASSIS device, subcommand 2 8091170c2feSWarner Losh * as described in IPMI v2.0 spec section 28.3. 8101170c2feSWarner Losh */ 8111170c2feSWarner Losh IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_CHASSIS_REQUEST, 0), 8121170c2feSWarner Losh IPMI_CHASSIS_CONTROL, 1, 0); 8131170c2feSWarner Losh req->ir_request[0] = IPMI_CC_POWER_CYCLE; 8141170c2feSWarner Losh 8151170c2feSWarner Losh ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); 8161170c2feSWarner Losh 8171170c2feSWarner Losh if (req->ir_error != 0 || req->ir_compcode != 0) { 8181170c2feSWarner Losh device_printf(sc->ipmi_dev, "Power cycling via IPMI failed code %#x %#x\n", 8191170c2feSWarner Losh req->ir_error, req->ir_compcode); 8201170c2feSWarner Losh return; 8211170c2feSWarner Losh } 8221170c2feSWarner Losh 8231170c2feSWarner Losh /* 82416f0063eSWarner Losh * BMCs are notoriously slow, give it cyle_wait seconds for the power 8251170c2feSWarner Losh * down leg of the power cycle. If that fails, fallback to the next 8261170c2feSWarner Losh * hanlder in the shutdown_final chain and/or the platform failsafe. 8271170c2feSWarner Losh */ 82816f0063eSWarner Losh DELAY(cycle_wait * 1000 * 1000); 8291170c2feSWarner Losh device_printf(sc->ipmi_dev, "Power cycling via IPMI timed out\n"); 8301170c2feSWarner Losh } 8311170c2feSWarner Losh 8321170c2feSWarner Losh static void 833d72a0786SJohn Baldwin ipmi_startup(void *arg) 834d72a0786SJohn Baldwin { 835d72a0786SJohn Baldwin struct ipmi_softc *sc = arg; 836d72a0786SJohn Baldwin struct ipmi_request *req; 837d72a0786SJohn Baldwin device_t dev; 838d72a0786SJohn Baldwin int error, i; 839d72a0786SJohn Baldwin 840d72a0786SJohn Baldwin config_intrhook_disestablish(&sc->ipmi_ich); 841d72a0786SJohn Baldwin dev = sc->ipmi_dev; 842d72a0786SJohn Baldwin 843d72a0786SJohn Baldwin /* Initialize interface-independent state. */ 844c869aa71SJohn Baldwin mtx_init(&sc->ipmi_requests_lock, "ipmi requests", NULL, MTX_DEF); 845c869aa71SJohn Baldwin mtx_init(&sc->ipmi_io_lock, "ipmi io", NULL, MTX_DEF); 846d72a0786SJohn Baldwin cv_init(&sc->ipmi_request_added, "ipmireq"); 847d72a0786SJohn Baldwin TAILQ_INIT(&sc->ipmi_pending_requests); 848d72a0786SJohn Baldwin 849d72a0786SJohn Baldwin /* Initialize interface-dependent state. */ 850d72a0786SJohn Baldwin error = sc->ipmi_startup(sc); 851d72a0786SJohn Baldwin if (error) { 852d72a0786SJohn Baldwin device_printf(dev, "Failed to initialize interface: %d\n", 853d72a0786SJohn Baldwin error); 854d72a0786SJohn Baldwin return; 855d72a0786SJohn Baldwin } 856d72a0786SJohn Baldwin 857d72a0786SJohn Baldwin /* Send a GET_DEVICE_ID request. */ 858c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 859d72a0786SJohn Baldwin IPMI_GET_DEVICE_ID, 0, 15); 860d72a0786SJohn Baldwin 861d72a0786SJohn Baldwin error = ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); 862d72a0786SJohn Baldwin if (error == EWOULDBLOCK) { 863d72a0786SJohn Baldwin device_printf(dev, "Timed out waiting for GET_DEVICE_ID\n"); 864d72a0786SJohn Baldwin return; 865d72a0786SJohn Baldwin } else if (error) { 866d72a0786SJohn Baldwin device_printf(dev, "Failed GET_DEVICE_ID: %d\n", error); 867d72a0786SJohn Baldwin return; 868d72a0786SJohn Baldwin } else if (req->ir_compcode != 0) { 869d72a0786SJohn Baldwin device_printf(dev, 870d72a0786SJohn Baldwin "Bad completion code for GET_DEVICE_ID: %d\n", 871d72a0786SJohn Baldwin req->ir_compcode); 872d72a0786SJohn Baldwin return; 873d72a0786SJohn Baldwin } else if (req->ir_replylen < 5) { 874d72a0786SJohn Baldwin device_printf(dev, "Short reply for GET_DEVICE_ID: %d\n", 875d72a0786SJohn Baldwin req->ir_replylen); 876d72a0786SJohn Baldwin return; 877d72a0786SJohn Baldwin } 878d72a0786SJohn Baldwin 87914689886SRuslan Ermilov device_printf(dev, "IPMI device rev. %d, firmware rev. %d.%d%d, " 8801170c2feSWarner Losh "version %d.%d, device support mask %#x\n", 881d72a0786SJohn Baldwin req->ir_reply[1] & 0x0f, 88214689886SRuslan Ermilov req->ir_reply[2] & 0x7f, req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f, 8831170c2feSWarner Losh req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4, req->ir_reply[5]); 8841170c2feSWarner Losh 8851170c2feSWarner Losh sc->ipmi_dev_support = req->ir_reply[5]; 886d72a0786SJohn Baldwin 887c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 888d72a0786SJohn Baldwin IPMI_CLEAR_FLAGS, 1, 0); 889d72a0786SJohn Baldwin 890d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req, 0); 891d72a0786SJohn Baldwin 892d72a0786SJohn Baldwin /* XXX: Magic numbers */ 893d72a0786SJohn Baldwin if (req->ir_compcode == 0xc0) { 894d72a0786SJohn Baldwin device_printf(dev, "Clear flags is busy\n"); 895d72a0786SJohn Baldwin } 896d72a0786SJohn Baldwin if (req->ir_compcode == 0xc1) { 897d72a0786SJohn Baldwin device_printf(dev, "Clear flags illegal\n"); 898d72a0786SJohn Baldwin } 899d72a0786SJohn Baldwin 900d72a0786SJohn Baldwin for (i = 0; i < 8; i++) { 901c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 902d72a0786SJohn Baldwin IPMI_GET_CHANNEL_INFO, 1, 0); 903d72a0786SJohn Baldwin req->ir_request[0] = i; 904d72a0786SJohn Baldwin 9059d3b47abSAlexander Motin error = ipmi_submit_driver_request(sc, req, 0); 906d72a0786SJohn Baldwin 9079d3b47abSAlexander Motin if (error != 0 || req->ir_compcode != 0) 908d72a0786SJohn Baldwin break; 909d72a0786SJohn Baldwin } 910d72a0786SJohn Baldwin device_printf(dev, "Number of channels %d\n", i); 911d72a0786SJohn Baldwin 9129662eef5SJohn Baldwin /* 9139662eef5SJohn Baldwin * Probe for watchdog, but only for backends which support 9149662eef5SJohn Baldwin * polled driver requests. 9159662eef5SJohn Baldwin */ 916e3500c60SWojciech Macek if (wd_init_enable && sc->ipmi_driver_requests_polled) { 917c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 918d72a0786SJohn Baldwin IPMI_GET_WDOG, 0, 0); 919d72a0786SJohn Baldwin 9209d3b47abSAlexander Motin error = ipmi_submit_driver_request(sc, req, 0); 921d72a0786SJohn Baldwin 9229d3b47abSAlexander Motin if (error == 0 && req->ir_compcode == 0x00) { 923d72a0786SJohn Baldwin device_printf(dev, "Attached watchdog\n"); 924d72a0786SJohn Baldwin /* register the watchdog event handler */ 9259662eef5SJohn Baldwin sc->ipmi_watchdog_tag = EVENTHANDLER_REGISTER( 9269662eef5SJohn Baldwin watchdog_list, ipmi_wd_event, sc, 0); 92714d00450SWarner Losh sc->ipmi_shutdown_tag = EVENTHANDLER_REGISTER( 92814d00450SWarner Losh shutdown_pre_sync, ipmi_shutdown_event, 92914d00450SWarner Losh sc, 0); 9309662eef5SJohn Baldwin } 931d72a0786SJohn Baldwin } 932d72a0786SJohn Baldwin 933943bebd2SJohn Baldwin sc->ipmi_cdev = make_dev(&ipmi_cdevsw, device_get_unit(dev), 934d72a0786SJohn Baldwin UID_ROOT, GID_OPERATOR, 0660, "ipmi%d", device_get_unit(dev)); 935943bebd2SJohn Baldwin if (sc->ipmi_cdev == NULL) { 936d72a0786SJohn Baldwin device_printf(dev, "Failed to create cdev\n"); 937d72a0786SJohn Baldwin return; 938d72a0786SJohn Baldwin } 939943bebd2SJohn Baldwin sc->ipmi_cdev->si_drv1 = sc; 9401170c2feSWarner Losh 9411170c2feSWarner Losh /* 94214d00450SWarner Losh * Set initial watchdog state. If desired, set an initial 94314d00450SWarner Losh * watchdog on startup. Or, if the watchdog device is 94414d00450SWarner Losh * disabled, clear any existing watchdog. 94514d00450SWarner Losh */ 94614d00450SWarner Losh if (on && wd_startup_countdown > 0) { 94714d00450SWarner Losh if (ipmi_set_watchdog(sc, wd_startup_countdown) == 0 && 94814d00450SWarner Losh ipmi_reset_watchdog(sc) == 0) { 94914d00450SWarner Losh sc->ipmi_watchdog_active = wd_startup_countdown; 95014d00450SWarner Losh sc->ipmi_watchdog_actions = wd_timer_actions; 95114d00450SWarner Losh sc->ipmi_watchdog_pretimeout = wd_pretimeout_countdown; 95214d00450SWarner Losh } else 95314d00450SWarner Losh (void)ipmi_set_watchdog(sc, 0); 95414d00450SWarner Losh ipmi_reset_watchdog(sc); 95514d00450SWarner Losh } else if (!on) 95614d00450SWarner Losh (void)ipmi_set_watchdog(sc, 0); 95714d00450SWarner Losh /* 95826649bb5SConrad Meyer * Power cycle the system off using IPMI. We use last - 2 since we don't 9591170c2feSWarner Losh * handle all the other kinds of reboots. We'll let others handle them. 9601170c2feSWarner Losh * We only try to do this if the BMC supports the Chassis device. 9611170c2feSWarner Losh */ 9621170c2feSWarner Losh if (sc->ipmi_dev_support & IPMI_ADS_CHASSIS) { 9631170c2feSWarner Losh device_printf(dev, "Establishing power cycle handler\n"); 9641170c2feSWarner Losh sc->ipmi_power_cycle_tag = EVENTHANDLER_REGISTER(shutdown_final, 96526649bb5SConrad Meyer ipmi_power_cycle, sc, SHUTDOWN_PRI_LAST - 2); 9661170c2feSWarner Losh } 967d72a0786SJohn Baldwin } 968d72a0786SJohn Baldwin 96937b1ce13SDoug Ambrisko int 97037b1ce13SDoug Ambrisko ipmi_attach(device_t dev) 97137b1ce13SDoug Ambrisko { 97237b1ce13SDoug Ambrisko struct ipmi_softc *sc = device_get_softc(dev); 973d72a0786SJohn Baldwin int error; 97437b1ce13SDoug Ambrisko 975d72a0786SJohn Baldwin if (sc->ipmi_irq_res != NULL && sc->ipmi_intr != NULL) { 976d72a0786SJohn Baldwin error = bus_setup_intr(dev, sc->ipmi_irq_res, INTR_TYPE_MISC, 977ef544f63SPaolo Pisati NULL, sc->ipmi_intr, sc, &sc->ipmi_irq); 978d72a0786SJohn Baldwin if (error) { 979d72a0786SJohn Baldwin device_printf(dev, "can't set up interrupt\n"); 980d72a0786SJohn Baldwin return (error); 98137b1ce13SDoug Ambrisko } 98237b1ce13SDoug Ambrisko } 98337b1ce13SDoug Ambrisko 984d72a0786SJohn Baldwin bzero(&sc->ipmi_ich, sizeof(struct intr_config_hook)); 985d72a0786SJohn Baldwin sc->ipmi_ich.ich_func = ipmi_startup; 986d72a0786SJohn Baldwin sc->ipmi_ich.ich_arg = sc; 987d72a0786SJohn Baldwin if (config_intrhook_establish(&sc->ipmi_ich) != 0) { 988d72a0786SJohn Baldwin device_printf(dev, "can't establish configuration hook\n"); 989d72a0786SJohn Baldwin return (ENOMEM); 99037b1ce13SDoug Ambrisko } 99137b1ce13SDoug Ambrisko 99237b1ce13SDoug Ambrisko ipmi_attached = 1; 993d72a0786SJohn Baldwin return (0); 99437b1ce13SDoug Ambrisko } 99537b1ce13SDoug Ambrisko 99637b1ce13SDoug Ambrisko int 99737b1ce13SDoug Ambrisko ipmi_detach(device_t dev) 99837b1ce13SDoug Ambrisko { 99937b1ce13SDoug Ambrisko struct ipmi_softc *sc; 100037b1ce13SDoug Ambrisko 100137b1ce13SDoug Ambrisko sc = device_get_softc(dev); 1002d72a0786SJohn Baldwin 1003d72a0786SJohn Baldwin /* Fail if there are any open handles. */ 1004d72a0786SJohn Baldwin IPMI_LOCK(sc); 1005943bebd2SJohn Baldwin if (sc->ipmi_opened) { 1006d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 1007d72a0786SJohn Baldwin return (EBUSY); 1008d72a0786SJohn Baldwin } 1009d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 1010943bebd2SJohn Baldwin if (sc->ipmi_cdev) 1011943bebd2SJohn Baldwin destroy_dev(sc->ipmi_cdev); 1012d72a0786SJohn Baldwin 1013d72a0786SJohn Baldwin /* Detach from watchdog handling and turn off watchdog. */ 101414d00450SWarner Losh if (sc->ipmi_shutdown_tag) 101514d00450SWarner Losh EVENTHANDLER_DEREGISTER(shutdown_pre_sync, 101614d00450SWarner Losh sc->ipmi_shutdown_tag); 1017d72a0786SJohn Baldwin if (sc->ipmi_watchdog_tag) { 1018d72a0786SJohn Baldwin EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_watchdog_tag); 1019d72a0786SJohn Baldwin ipmi_set_watchdog(sc, 0); 1020d72a0786SJohn Baldwin } 1021d72a0786SJohn Baldwin 10221170c2feSWarner Losh /* Detach from shutdown handling for power cycle reboot */ 10231170c2feSWarner Losh if (sc->ipmi_power_cycle_tag) 10241170c2feSWarner Losh EVENTHANDLER_DEREGISTER(shutdown_final, sc->ipmi_power_cycle_tag); 10251170c2feSWarner Losh 1026d72a0786SJohn Baldwin /* XXX: should use shutdown callout I think. */ 1027d72a0786SJohn Baldwin /* If the backend uses a kthread, shut it down. */ 1028d72a0786SJohn Baldwin IPMI_LOCK(sc); 1029d72a0786SJohn Baldwin sc->ipmi_detaching = 1; 1030d72a0786SJohn Baldwin if (sc->ipmi_kthread) { 1031d72a0786SJohn Baldwin cv_broadcast(&sc->ipmi_request_added); 1032c869aa71SJohn Baldwin msleep(sc->ipmi_kthread, &sc->ipmi_requests_lock, 0, 1033c869aa71SJohn Baldwin "ipmi_wait", 0); 1034d72a0786SJohn Baldwin } 1035d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 1036d72a0786SJohn Baldwin if (sc->ipmi_irq) 1037d72a0786SJohn Baldwin bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); 1038d72a0786SJohn Baldwin 1039d72a0786SJohn Baldwin ipmi_release_resources(dev); 1040c869aa71SJohn Baldwin mtx_destroy(&sc->ipmi_io_lock); 1041c869aa71SJohn Baldwin mtx_destroy(&sc->ipmi_requests_lock); 1042d72a0786SJohn Baldwin return (0); 1043d72a0786SJohn Baldwin } 1044d72a0786SJohn Baldwin 1045d72a0786SJohn Baldwin void 1046d72a0786SJohn Baldwin ipmi_release_resources(device_t dev) 1047d72a0786SJohn Baldwin { 1048d72a0786SJohn Baldwin struct ipmi_softc *sc; 1049d72a0786SJohn Baldwin int i; 1050d72a0786SJohn Baldwin 1051d72a0786SJohn Baldwin sc = device_get_softc(dev); 1052d72a0786SJohn Baldwin if (sc->ipmi_irq) 1053d72a0786SJohn Baldwin bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); 1054d72a0786SJohn Baldwin if (sc->ipmi_irq_res) 1055d72a0786SJohn Baldwin bus_release_resource(dev, SYS_RES_IRQ, sc->ipmi_irq_rid, 1056d72a0786SJohn Baldwin sc->ipmi_irq_res); 1057d72a0786SJohn Baldwin for (i = 0; i < MAX_RES; i++) 1058d72a0786SJohn Baldwin if (sc->ipmi_io_res[i]) 1059d72a0786SJohn Baldwin bus_release_resource(dev, sc->ipmi_io_type, 1060d72a0786SJohn Baldwin sc->ipmi_io_rid + i, sc->ipmi_io_res[i]); 1061d72a0786SJohn Baldwin } 1062d72a0786SJohn Baldwin 1063d72a0786SJohn Baldwin devclass_t ipmi_devclass; 1064d72a0786SJohn Baldwin 1065d72a0786SJohn Baldwin /* XXX: Why? */ 1066d72a0786SJohn Baldwin static void 1067d72a0786SJohn Baldwin ipmi_unload(void *arg) 1068d72a0786SJohn Baldwin { 1069d72a0786SJohn Baldwin device_t * devs; 1070d72a0786SJohn Baldwin int count; 1071d72a0786SJohn Baldwin int i; 1072d72a0786SJohn Baldwin 1073*ac56d90aSJohn Baldwin if (devclass_get_devices(devclass_find("ipmi"), &devs, &count) != 0) 1074bec0c98eSJohn Baldwin return; 1075d72a0786SJohn Baldwin for (i = 0; i < count; i++) 1076d72a0786SJohn Baldwin device_delete_child(device_get_parent(devs[i]), devs[i]); 1077bec0c98eSJohn Baldwin free(devs, M_TEMP); 1078d72a0786SJohn Baldwin } 1079d72a0786SJohn Baldwin SYSUNINIT(ipmi_unload, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipmi_unload, NULL); 1080d72a0786SJohn Baldwin 108119d61f3fSDoug Ambrisko #ifdef IMPI_DEBUG 108237b1ce13SDoug Ambrisko static void 1083d72a0786SJohn Baldwin dump_buf(u_char *data, int len) 1084d72a0786SJohn Baldwin { 108537b1ce13SDoug Ambrisko char buf[20]; 108637b1ce13SDoug Ambrisko char line[1024]; 108737b1ce13SDoug Ambrisko char temp[30]; 108837b1ce13SDoug Ambrisko int count = 0; 108937b1ce13SDoug Ambrisko int i=0; 109037b1ce13SDoug Ambrisko 109137b1ce13SDoug Ambrisko printf("Address %p len %d\n", data, len); 109237b1ce13SDoug Ambrisko if (len > 256) 109337b1ce13SDoug Ambrisko len = 256; 109437b1ce13SDoug Ambrisko line[0] = '\000'; 109537b1ce13SDoug Ambrisko for (; len > 0; len--, data++) { 109637b1ce13SDoug Ambrisko sprintf(temp, "%02x ", *data); 109737b1ce13SDoug Ambrisko strcat(line, temp); 109837b1ce13SDoug Ambrisko if (*data >= ' ' && *data <= '~') 109937b1ce13SDoug Ambrisko buf[count] = *data; 110037b1ce13SDoug Ambrisko else if (*data >= 'A' && *data <= 'Z') 110137b1ce13SDoug Ambrisko buf[count] = *data; 110237b1ce13SDoug Ambrisko else 110337b1ce13SDoug Ambrisko buf[count] = '.'; 110437b1ce13SDoug Ambrisko if (++count == 16) { 110537b1ce13SDoug Ambrisko buf[count] = '\000'; 110637b1ce13SDoug Ambrisko count = 0; 110737b1ce13SDoug Ambrisko printf(" %3x %s %s\n", i, line, buf); 110837b1ce13SDoug Ambrisko i+=16; 110937b1ce13SDoug Ambrisko line[0] = '\000'; 111037b1ce13SDoug Ambrisko } 111137b1ce13SDoug Ambrisko } 111237b1ce13SDoug Ambrisko buf[count] = '\000'; 111337b1ce13SDoug Ambrisko 111437b1ce13SDoug Ambrisko for (; count != 16; count++) { 111537b1ce13SDoug Ambrisko strcat(line, " "); 111637b1ce13SDoug Ambrisko } 111737b1ce13SDoug Ambrisko printf(" %3x %s %s\n", i, line, buf); 111837b1ce13SDoug Ambrisko } 111937b1ce13SDoug Ambrisko #endif 1120