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> 37*e2e050c8SConrad Meyer #include <sys/eventhandler.h> 3837b1ce13SDoug Ambrisko #include <sys/kernel.h> 39*e2e050c8SConrad Meyer #include <sys/lock.h> 4037b1ce13SDoug Ambrisko #include <sys/malloc.h> 4137b1ce13SDoug Ambrisko #include <sys/module.h> 42*e2e050c8SConrad 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 58c869aa71SJohn Baldwin /* 59c869aa71SJohn Baldwin * Driver request structures are allocated on the stack via alloca() to 60c869aa71SJohn Baldwin * avoid calling malloc(), especially for the watchdog handler. 61c869aa71SJohn Baldwin * To avoid too much stack growth, a previously allocated structure can 62c869aa71SJohn Baldwin * be reused via IPMI_INIT_DRIVER_REQUEST(), but the caller should ensure 63c869aa71SJohn Baldwin * that there is adequate reply/request space in the original allocation. 64c869aa71SJohn Baldwin */ 65c869aa71SJohn Baldwin #define IPMI_INIT_DRIVER_REQUEST(req, addr, cmd, reqlen, replylen) \ 66c869aa71SJohn Baldwin bzero((req), sizeof(struct ipmi_request)); \ 67c869aa71SJohn Baldwin ipmi_init_request((req), NULL, 0, (addr), (cmd), (reqlen), (replylen)) 68c869aa71SJohn Baldwin 69c869aa71SJohn Baldwin #define IPMI_ALLOC_DRIVER_REQUEST(req, addr, cmd, reqlen, replylen) \ 70c869aa71SJohn Baldwin (req) = __builtin_alloca(sizeof(struct ipmi_request) + \ 71c869aa71SJohn Baldwin (reqlen) + (replylen)); \ 72c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST((req), (addr), (cmd), (reqlen), \ 73c869aa71SJohn Baldwin (replylen)) 74c869aa71SJohn Baldwin 7537b1ce13SDoug Ambrisko #ifdef IPMB 7637b1ce13SDoug Ambrisko static int ipmi_ipmb_checksum(u_char, int); 7737b1ce13SDoug Ambrisko static int ipmi_ipmb_send_message(device_t, u_char, u_char, u_char, 7837b1ce13SDoug Ambrisko u_char, u_char, int) 7937b1ce13SDoug Ambrisko #endif 8037b1ce13SDoug Ambrisko 8137b1ce13SDoug Ambrisko static d_ioctl_t ipmi_ioctl; 8237b1ce13SDoug Ambrisko static d_poll_t ipmi_poll; 8337b1ce13SDoug Ambrisko static d_open_t ipmi_open; 84943bebd2SJohn Baldwin static void ipmi_dtor(void *arg); 8537b1ce13SDoug Ambrisko 8637b1ce13SDoug Ambrisko int ipmi_attached = 0; 8737b1ce13SDoug Ambrisko 8837b1ce13SDoug Ambrisko static int on = 1; 8914d00450SWarner Losh static bool wd_in_shutdown = false; 9014d00450SWarner Losh static int wd_timer_actions = IPMI_SET_WD_ACTION_POWER_CYCLE; 919ee3ea71SPeter Wemm static int wd_shutdown_countdown = 0; /* sec */ 92c154763dSWarner Losh static int wd_startup_countdown = 0; /* sec */ 9314d00450SWarner Losh static int wd_pretimeout_countdown = 120; /* sec */ 9416f0063eSWarner Losh static int cycle_wait = 10; /* sec */ 9514d00450SWarner Losh 966472ac3dSEd Schouten static SYSCTL_NODE(_hw, OID_AUTO, ipmi, CTLFLAG_RD, 0, 976472ac3dSEd Schouten "IPMI driver parameters"); 9814d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RWTUN, 9937b1ce13SDoug Ambrisko &on, 0, ""); 10014d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_timer_actions, CTLFLAG_RW, 10114d00450SWarner Losh &wd_timer_actions, 0, 10214d00450SWarner Losh "IPMI watchdog timer actions (including pre-timeout interrupt)"); 10314d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_shutdown_countdown, CTLFLAG_RW, 10414d00450SWarner Losh &wd_shutdown_countdown, 0, 10514d00450SWarner Losh "IPMI watchdog countdown for shutdown (seconds)"); 10614d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_startup_countdown, CTLFLAG_RDTUN, 10714d00450SWarner Losh &wd_startup_countdown, 0, 10814d00450SWarner Losh "IPMI watchdog countdown initialized during startup (seconds)"); 10914d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_pretimeout_countdown, CTLFLAG_RW, 11014d00450SWarner Losh &wd_pretimeout_countdown, 0, 11114d00450SWarner Losh "IPMI watchdog pre-timeout countdown (seconds)"); 11216f0063eSWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, cyle_wait, CTLFLAG_RWTUN, 11316f0063eSWarner Losh &cycle_wait, 0, 11416f0063eSWarner Losh "IPMI power cycle on reboot delay time (seconds)"); 11537b1ce13SDoug Ambrisko 11637b1ce13SDoug Ambrisko static struct cdevsw ipmi_cdevsw = { 11737b1ce13SDoug Ambrisko .d_version = D_VERSION, 11837b1ce13SDoug Ambrisko .d_open = ipmi_open, 11937b1ce13SDoug Ambrisko .d_ioctl = ipmi_ioctl, 12037b1ce13SDoug Ambrisko .d_poll = ipmi_poll, 12137b1ce13SDoug Ambrisko .d_name = "ipmi", 12237b1ce13SDoug Ambrisko }; 12337b1ce13SDoug Ambrisko 124d745c852SEd Schouten static MALLOC_DEFINE(M_IPMI, "ipmi", "ipmi"); 12537b1ce13SDoug Ambrisko 12637b1ce13SDoug Ambrisko static int 127d72a0786SJohn Baldwin ipmi_open(struct cdev *cdev, int flags, int fmt, struct thread *td) 12837b1ce13SDoug Ambrisko { 129d72a0786SJohn Baldwin struct ipmi_device *dev; 13037b1ce13SDoug Ambrisko struct ipmi_softc *sc; 131943bebd2SJohn Baldwin int error; 13237b1ce13SDoug Ambrisko 13337b1ce13SDoug Ambrisko if (!on) 134d72a0786SJohn Baldwin return (ENOENT); 13537b1ce13SDoug Ambrisko 136943bebd2SJohn Baldwin /* Initialize the per file descriptor data. */ 137943bebd2SJohn Baldwin dev = malloc(sizeof(struct ipmi_device), M_IPMI, M_WAITOK | M_ZERO); 138943bebd2SJohn Baldwin error = devfs_set_cdevpriv(dev, ipmi_dtor); 139943bebd2SJohn Baldwin if (error) { 140943bebd2SJohn Baldwin free(dev, M_IPMI); 141943bebd2SJohn Baldwin return (error); 14237b1ce13SDoug Ambrisko } 143943bebd2SJohn Baldwin 144943bebd2SJohn Baldwin sc = cdev->si_drv1; 145943bebd2SJohn Baldwin TAILQ_INIT(&dev->ipmi_completed_requests); 146943bebd2SJohn Baldwin dev->ipmi_address = IPMI_BMC_SLAVE_ADDR; 147943bebd2SJohn Baldwin dev->ipmi_lun = IPMI_BMC_SMS_LUN; 148943bebd2SJohn Baldwin dev->ipmi_softc = sc; 149943bebd2SJohn Baldwin IPMI_LOCK(sc); 150943bebd2SJohn Baldwin sc->ipmi_opened++; 151d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 15237b1ce13SDoug Ambrisko 153d72a0786SJohn Baldwin return (0); 15437b1ce13SDoug Ambrisko } 15537b1ce13SDoug Ambrisko 15637b1ce13SDoug Ambrisko static int 157d72a0786SJohn Baldwin ipmi_poll(struct cdev *cdev, int poll_events, struct thread *td) 15837b1ce13SDoug Ambrisko { 159d72a0786SJohn Baldwin struct ipmi_device *dev; 16037b1ce13SDoug Ambrisko struct ipmi_softc *sc; 16137b1ce13SDoug Ambrisko int revents = 0; 16237b1ce13SDoug Ambrisko 163943bebd2SJohn Baldwin if (devfs_get_cdevpriv((void **)&dev)) 164943bebd2SJohn Baldwin return (0); 16537b1ce13SDoug Ambrisko 166943bebd2SJohn Baldwin sc = cdev->si_drv1; 167d72a0786SJohn Baldwin IPMI_LOCK(sc); 16837b1ce13SDoug Ambrisko if (poll_events & (POLLIN | POLLRDNORM)) { 169d72a0786SJohn Baldwin if (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) 17037b1ce13SDoug Ambrisko revents |= poll_events & (POLLIN | POLLRDNORM); 171d72a0786SJohn Baldwin if (dev->ipmi_requests == 0) 17237b1ce13SDoug Ambrisko revents |= POLLERR; 17337b1ce13SDoug Ambrisko } 17437b1ce13SDoug Ambrisko 17537b1ce13SDoug Ambrisko if (revents == 0) { 17637b1ce13SDoug Ambrisko if (poll_events & (POLLIN | POLLRDNORM)) 177d72a0786SJohn Baldwin selrecord(td, &dev->ipmi_select); 178d72a0786SJohn Baldwin } 179d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 180d72a0786SJohn Baldwin 181d72a0786SJohn Baldwin return (revents); 18237b1ce13SDoug Ambrisko } 18337b1ce13SDoug Ambrisko 184d72a0786SJohn Baldwin static void 185d72a0786SJohn Baldwin ipmi_purge_completed_requests(struct ipmi_device *dev) 186d72a0786SJohn Baldwin { 187d72a0786SJohn Baldwin struct ipmi_request *req; 188d72a0786SJohn Baldwin 189d72a0786SJohn Baldwin while (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) { 190d72a0786SJohn Baldwin req = TAILQ_FIRST(&dev->ipmi_completed_requests); 191d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, req, ir_link); 192d72a0786SJohn Baldwin dev->ipmi_requests--; 193d72a0786SJohn Baldwin ipmi_free_request(req); 194d72a0786SJohn Baldwin } 19537b1ce13SDoug Ambrisko } 19637b1ce13SDoug Ambrisko 197943bebd2SJohn Baldwin static void 198943bebd2SJohn Baldwin ipmi_dtor(void *arg) 19937b1ce13SDoug Ambrisko { 200d72a0786SJohn Baldwin struct ipmi_request *req, *nreq; 201d72a0786SJohn Baldwin struct ipmi_device *dev; 20237b1ce13SDoug Ambrisko struct ipmi_softc *sc; 20337b1ce13SDoug Ambrisko 204943bebd2SJohn Baldwin dev = arg; 205d72a0786SJohn Baldwin sc = dev->ipmi_softc; 20637b1ce13SDoug Ambrisko 207d72a0786SJohn Baldwin IPMI_LOCK(sc); 208d72a0786SJohn Baldwin if (dev->ipmi_requests) { 209d72a0786SJohn Baldwin /* Throw away any pending requests for this device. */ 210d72a0786SJohn Baldwin TAILQ_FOREACH_SAFE(req, &sc->ipmi_pending_requests, ir_link, 211d72a0786SJohn Baldwin nreq) { 212d72a0786SJohn Baldwin if (req->ir_owner == dev) { 213d72a0786SJohn Baldwin TAILQ_REMOVE(&sc->ipmi_pending_requests, req, 214d72a0786SJohn Baldwin ir_link); 215d72a0786SJohn Baldwin dev->ipmi_requests--; 216d72a0786SJohn Baldwin ipmi_free_request(req); 217d72a0786SJohn Baldwin } 218d72a0786SJohn Baldwin } 21937b1ce13SDoug Ambrisko 220d72a0786SJohn Baldwin /* Throw away any pending completed requests for this device. */ 221d72a0786SJohn Baldwin ipmi_purge_completed_requests(dev); 222d72a0786SJohn Baldwin 223d72a0786SJohn Baldwin /* 224d72a0786SJohn Baldwin * If we still have outstanding requests, they must be stuck 225d72a0786SJohn Baldwin * in an interface driver, so wait for those to drain. 226d72a0786SJohn Baldwin */ 227d72a0786SJohn Baldwin dev->ipmi_closing = 1; 228d72a0786SJohn Baldwin while (dev->ipmi_requests > 0) { 229c869aa71SJohn Baldwin msleep(&dev->ipmi_requests, &sc->ipmi_requests_lock, 230c869aa71SJohn Baldwin PWAIT, "ipmidrain", 0); 231d72a0786SJohn Baldwin ipmi_purge_completed_requests(dev); 232d72a0786SJohn Baldwin } 233d72a0786SJohn Baldwin } 234943bebd2SJohn Baldwin sc->ipmi_opened--; 235d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 236d72a0786SJohn Baldwin 237d72a0786SJohn Baldwin /* Cleanup. */ 238d72a0786SJohn Baldwin free(dev, M_IPMI); 23937b1ce13SDoug Ambrisko } 24037b1ce13SDoug Ambrisko 24137b1ce13SDoug Ambrisko #ifdef IPMB 24237b1ce13SDoug Ambrisko static int 24337b1ce13SDoug Ambrisko ipmi_ipmb_checksum(u_char *data, int len) 24437b1ce13SDoug Ambrisko { 24537b1ce13SDoug Ambrisko u_char sum = 0; 24637b1ce13SDoug Ambrisko 24737b1ce13SDoug Ambrisko for (; len; len--) { 24837b1ce13SDoug Ambrisko sum += *data++; 24937b1ce13SDoug Ambrisko } 250d72a0786SJohn Baldwin return (-sum); 25137b1ce13SDoug Ambrisko } 25237b1ce13SDoug Ambrisko 253d72a0786SJohn Baldwin /* XXX: Needs work */ 25437b1ce13SDoug Ambrisko static int 25537b1ce13SDoug Ambrisko ipmi_ipmb_send_message(device_t dev, u_char channel, u_char netfn, 25637b1ce13SDoug Ambrisko u_char command, u_char seq, u_char *data, int data_len) 25737b1ce13SDoug Ambrisko { 25837b1ce13SDoug Ambrisko struct ipmi_softc *sc = device_get_softc(dev); 259d72a0786SJohn Baldwin struct ipmi_request *req; 26037b1ce13SDoug Ambrisko u_char slave_addr = 0x52; 261d72a0786SJohn Baldwin int error; 26237b1ce13SDoug Ambrisko 263c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 264d72a0786SJohn Baldwin IPMI_SEND_MSG, data_len + 8, 0); 265d72a0786SJohn Baldwin req->ir_request[0] = channel; 266d72a0786SJohn Baldwin req->ir_request[1] = slave_addr; 267d72a0786SJohn Baldwin req->ir_request[2] = IPMI_ADDR(netfn, 0); 268d72a0786SJohn Baldwin req->ir_request[3] = ipmi_ipmb_checksum(&req->ir_request[1], 2); 269d72a0786SJohn Baldwin req->ir_request[4] = sc->ipmi_address; 270d72a0786SJohn Baldwin req->ir_request[5] = IPMI_ADDR(seq, sc->ipmi_lun); 271d72a0786SJohn Baldwin req->ir_request[6] = command; 27237b1ce13SDoug Ambrisko 273d72a0786SJohn Baldwin bcopy(data, &req->ir_request[7], data_len); 274d72a0786SJohn Baldwin temp[data_len + 7] = ipmi_ipmb_checksum(&req->ir_request[4], 275d72a0786SJohn Baldwin data_len + 3); 27637b1ce13SDoug Ambrisko 277d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req); 278d72a0786SJohn Baldwin error = req->ir_error; 27937b1ce13SDoug Ambrisko 280d72a0786SJohn Baldwin return (error); 28137b1ce13SDoug Ambrisko } 28237b1ce13SDoug Ambrisko 28337b1ce13SDoug Ambrisko static int 284d72a0786SJohn Baldwin ipmi_handle_attn(struct ipmi_softc *sc) 28537b1ce13SDoug Ambrisko { 286d72a0786SJohn Baldwin struct ipmi_request *req; 28737b1ce13SDoug Ambrisko int error; 28837b1ce13SDoug Ambrisko 28937b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "BMC has a message\n"); 290c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 291d72a0786SJohn Baldwin IPMI_GET_MSG_FLAGS, 0, 1); 29237b1ce13SDoug Ambrisko 293d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req); 294d72a0786SJohn Baldwin 295d72a0786SJohn Baldwin if (req->ir_error == 0 && req->ir_compcode == 0) { 296d72a0786SJohn Baldwin if (req->ir_reply[0] & IPMI_MSG_BUFFER_FULL) { 29737b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "message buffer full"); 29837b1ce13SDoug Ambrisko } 299d72a0786SJohn Baldwin if (req->ir_reply[0] & IPMI_WDT_PRE_TIMEOUT) { 30037b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, 30137b1ce13SDoug Ambrisko "watchdog about to go off"); 30237b1ce13SDoug Ambrisko } 303d72a0786SJohn Baldwin if (req->ir_reply[0] & IPMI_MSG_AVAILABLE) { 304c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, 305d72a0786SJohn Baldwin IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG, 0, 306d72a0786SJohn Baldwin 16); 30737b1ce13SDoug Ambrisko 30837b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "throw out message "); 30937b1ce13SDoug Ambrisko dump_buf(temp, 16); 31037b1ce13SDoug Ambrisko } 31137b1ce13SDoug Ambrisko } 312d72a0786SJohn Baldwin error = req->ir_error; 313d72a0786SJohn Baldwin 314d72a0786SJohn Baldwin return (error); 315d72a0786SJohn Baldwin } 316d72a0786SJohn Baldwin #endif 317d72a0786SJohn Baldwin 318d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 319d72a0786SJohn Baldwin #define PTRIN(p) ((void *)(uintptr_t)(p)) 320d72a0786SJohn Baldwin #define PTROUT(p) ((uintptr_t)(p)) 32137b1ce13SDoug Ambrisko #endif 32237b1ce13SDoug Ambrisko 32337b1ce13SDoug Ambrisko static int 324d72a0786SJohn Baldwin ipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, 32537b1ce13SDoug Ambrisko int flags, struct thread *td) 32637b1ce13SDoug Ambrisko { 32737b1ce13SDoug Ambrisko struct ipmi_softc *sc; 328d72a0786SJohn Baldwin struct ipmi_device *dev; 329d72a0786SJohn Baldwin struct ipmi_request *kreq; 33037b1ce13SDoug Ambrisko struct ipmi_req *req = (struct ipmi_req *)data; 33137b1ce13SDoug Ambrisko struct ipmi_recv *recv = (struct ipmi_recv *)data; 33237b1ce13SDoug Ambrisko struct ipmi_addr addr; 333d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 334d72a0786SJohn Baldwin struct ipmi_req32 *req32 = (struct ipmi_req32 *)data; 335d72a0786SJohn Baldwin struct ipmi_recv32 *recv32 = (struct ipmi_recv32 *)data; 336d72a0786SJohn Baldwin union { 337d72a0786SJohn Baldwin struct ipmi_req req; 338d72a0786SJohn Baldwin struct ipmi_recv recv; 339d72a0786SJohn Baldwin } thunk32; 340d72a0786SJohn Baldwin #endif 34137b1ce13SDoug Ambrisko int error, len; 34237b1ce13SDoug Ambrisko 343943bebd2SJohn Baldwin error = devfs_get_cdevpriv((void **)&dev); 344943bebd2SJohn Baldwin if (error) 345943bebd2SJohn Baldwin return (error); 346943bebd2SJohn Baldwin 347943bebd2SJohn Baldwin sc = cdev->si_drv1; 348d72a0786SJohn Baldwin 349d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 350d72a0786SJohn Baldwin /* Convert 32-bit structures to native. */ 351d72a0786SJohn Baldwin switch (cmd) { 352d72a0786SJohn Baldwin case IPMICTL_SEND_COMMAND_32: 353d72a0786SJohn Baldwin req = &thunk32.req; 354d72a0786SJohn Baldwin req->addr = PTRIN(req32->addr); 355d72a0786SJohn Baldwin req->addr_len = req32->addr_len; 356d72a0786SJohn Baldwin req->msgid = req32->msgid; 357d72a0786SJohn Baldwin req->msg.netfn = req32->msg.netfn; 358d72a0786SJohn Baldwin req->msg.cmd = req32->msg.cmd; 359d72a0786SJohn Baldwin req->msg.data_len = req32->msg.data_len; 360d72a0786SJohn Baldwin req->msg.data = PTRIN(req32->msg.data); 361d72a0786SJohn Baldwin break; 362d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 363d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 364d72a0786SJohn Baldwin recv = &thunk32.recv; 365d72a0786SJohn Baldwin recv->addr = PTRIN(recv32->addr); 366d72a0786SJohn Baldwin recv->addr_len = recv32->addr_len; 367d72a0786SJohn Baldwin recv->msg.data_len = recv32->msg.data_len; 368d72a0786SJohn Baldwin recv->msg.data = PTRIN(recv32->msg.data); 369d72a0786SJohn Baldwin break; 370d72a0786SJohn Baldwin } 371d72a0786SJohn Baldwin #endif 37237b1ce13SDoug Ambrisko 37337b1ce13SDoug Ambrisko switch (cmd) { 374d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 375d72a0786SJohn Baldwin case IPMICTL_SEND_COMMAND_32: 376d72a0786SJohn Baldwin #endif 37737b1ce13SDoug Ambrisko case IPMICTL_SEND_COMMAND: 378d72a0786SJohn Baldwin /* 379d72a0786SJohn Baldwin * XXX: Need to add proper handling of this. 380d72a0786SJohn Baldwin */ 38137b1ce13SDoug Ambrisko error = copyin(req->addr, &addr, sizeof(addr)); 382d72a0786SJohn Baldwin if (error) 383d72a0786SJohn Baldwin return (error); 384d72a0786SJohn Baldwin 385d72a0786SJohn Baldwin IPMI_LOCK(sc); 386d72a0786SJohn Baldwin /* clear out old stuff in queue of stuff done */ 387d72a0786SJohn Baldwin /* XXX: This seems odd. */ 388d72a0786SJohn Baldwin while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))) { 389d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, 390d72a0786SJohn Baldwin ir_link); 391d72a0786SJohn Baldwin dev->ipmi_requests--; 392d72a0786SJohn Baldwin ipmi_free_request(kreq); 39337b1ce13SDoug Ambrisko } 394d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 395d72a0786SJohn Baldwin 396d72a0786SJohn Baldwin kreq = ipmi_alloc_request(dev, req->msgid, 397d72a0786SJohn Baldwin IPMI_ADDR(req->msg.netfn, 0), req->msg.cmd, 398d72a0786SJohn Baldwin req->msg.data_len, IPMI_MAX_RX); 399d72a0786SJohn Baldwin error = copyin(req->msg.data, kreq->ir_request, 40037b1ce13SDoug Ambrisko req->msg.data_len); 401d72a0786SJohn Baldwin if (error) { 402d72a0786SJohn Baldwin ipmi_free_request(kreq); 403d72a0786SJohn Baldwin return (error); 40437b1ce13SDoug Ambrisko } 405d72a0786SJohn Baldwin IPMI_LOCK(sc); 406d72a0786SJohn Baldwin dev->ipmi_requests++; 407d72a0786SJohn Baldwin error = sc->ipmi_enqueue_request(sc, kreq); 408d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 409d72a0786SJohn Baldwin if (error) 410d72a0786SJohn Baldwin return (error); 411d72a0786SJohn Baldwin break; 412d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 413d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 414d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 415d72a0786SJohn Baldwin #endif 41637b1ce13SDoug Ambrisko case IPMICTL_RECEIVE_MSG_TRUNC: 41737b1ce13SDoug Ambrisko case IPMICTL_RECEIVE_MSG: 41837b1ce13SDoug Ambrisko error = copyin(recv->addr, &addr, sizeof(addr)); 419d72a0786SJohn Baldwin if (error) 420d72a0786SJohn Baldwin return (error); 421d72a0786SJohn Baldwin 422d72a0786SJohn Baldwin IPMI_LOCK(sc); 423d72a0786SJohn Baldwin kreq = TAILQ_FIRST(&dev->ipmi_completed_requests); 424d72a0786SJohn Baldwin if (kreq == NULL) { 425d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 426d72a0786SJohn Baldwin return (EAGAIN); 42737b1ce13SDoug Ambrisko } 428d72a0786SJohn Baldwin addr.channel = IPMI_BMC_CHANNEL; 429d72a0786SJohn Baldwin /* XXX */ 430d72a0786SJohn Baldwin recv->recv_type = IPMI_RESPONSE_RECV_TYPE; 431d72a0786SJohn Baldwin recv->msgid = kreq->ir_msgid; 432d72a0786SJohn Baldwin recv->msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2; 433d72a0786SJohn Baldwin recv->msg.cmd = kreq->ir_command; 434d72a0786SJohn Baldwin error = kreq->ir_error; 435d72a0786SJohn Baldwin if (error) { 436d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, 437d72a0786SJohn Baldwin ir_link); 438d72a0786SJohn Baldwin dev->ipmi_requests--; 439d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 440d72a0786SJohn Baldwin ipmi_free_request(kreq); 441d72a0786SJohn Baldwin return (error); 442d72a0786SJohn Baldwin } 443d72a0786SJohn Baldwin len = kreq->ir_replylen + 1; 444d72a0786SJohn Baldwin if (recv->msg.data_len < len && 445d72a0786SJohn Baldwin (cmd == IPMICTL_RECEIVE_MSG 446d72a0786SJohn Baldwin #ifdef IPMICTL_RECEIVE_MSG_32 447c12dbd1dSDavid E. O'Brien || cmd == IPMICTL_RECEIVE_MSG_32 448d72a0786SJohn Baldwin #endif 449d72a0786SJohn Baldwin )) { 450d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 451d72a0786SJohn Baldwin return (EMSGSIZE); 452d72a0786SJohn Baldwin } 453d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link); 454d72a0786SJohn Baldwin dev->ipmi_requests--; 455d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 45637b1ce13SDoug Ambrisko len = min(recv->msg.data_len, len); 45737b1ce13SDoug Ambrisko recv->msg.data_len = len; 45837b1ce13SDoug Ambrisko error = copyout(&addr, recv->addr,sizeof(addr)); 45937b1ce13SDoug Ambrisko if (error == 0) 460d72a0786SJohn Baldwin error = copyout(&kreq->ir_compcode, recv->msg.data, 1); 461d72a0786SJohn Baldwin if (error == 0) 462d72a0786SJohn Baldwin error = copyout(kreq->ir_reply, recv->msg.data + 1, 463d72a0786SJohn Baldwin len - 1); 464d72a0786SJohn Baldwin ipmi_free_request(kreq); 465d72a0786SJohn Baldwin if (error) 466d72a0786SJohn Baldwin return (error); 467d72a0786SJohn Baldwin break; 46837b1ce13SDoug Ambrisko case IPMICTL_SET_MY_ADDRESS_CMD: 469d72a0786SJohn Baldwin IPMI_LOCK(sc); 470d72a0786SJohn Baldwin dev->ipmi_address = *(int*)data; 471d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 472d72a0786SJohn Baldwin break; 47337b1ce13SDoug Ambrisko case IPMICTL_GET_MY_ADDRESS_CMD: 474d72a0786SJohn Baldwin IPMI_LOCK(sc); 475d72a0786SJohn Baldwin *(int*)data = dev->ipmi_address; 476d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 477d72a0786SJohn Baldwin break; 47837b1ce13SDoug Ambrisko case IPMICTL_SET_MY_LUN_CMD: 479d72a0786SJohn Baldwin IPMI_LOCK(sc); 480d72a0786SJohn Baldwin dev->ipmi_lun = *(int*)data & 0x3; 481d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 482d72a0786SJohn Baldwin break; 48337b1ce13SDoug Ambrisko case IPMICTL_GET_MY_LUN_CMD: 484d72a0786SJohn Baldwin IPMI_LOCK(sc); 485d72a0786SJohn Baldwin *(int*)data = dev->ipmi_lun; 486d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 487d72a0786SJohn Baldwin break; 48837b1ce13SDoug Ambrisko case IPMICTL_SET_GETS_EVENTS_CMD: 48937b1ce13SDoug Ambrisko /* 49037b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, 49137b1ce13SDoug Ambrisko "IPMICTL_SET_GETS_EVENTS_CMD NA\n"); 49237b1ce13SDoug Ambrisko */ 493d72a0786SJohn Baldwin break; 49437b1ce13SDoug Ambrisko case IPMICTL_REGISTER_FOR_CMD: 49537b1ce13SDoug Ambrisko case IPMICTL_UNREGISTER_FOR_CMD: 496d72a0786SJohn Baldwin return (EOPNOTSUPP); 497d72a0786SJohn Baldwin default: 49837b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "Unknown IOCTL %lX\n", cmd); 499d72a0786SJohn Baldwin return (ENOIOCTL); 50037b1ce13SDoug Ambrisko } 50137b1ce13SDoug Ambrisko 502d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 503d72a0786SJohn Baldwin /* Update changed fields in 32-bit structures. */ 504d72a0786SJohn Baldwin switch (cmd) { 505d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 506d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 507d72a0786SJohn Baldwin recv32->recv_type = recv->recv_type; 508d72a0786SJohn Baldwin recv32->msgid = recv->msgid; 509d72a0786SJohn Baldwin recv32->msg.netfn = recv->msg.netfn; 510d72a0786SJohn Baldwin recv32->msg.cmd = recv->msg.cmd; 511d72a0786SJohn Baldwin recv32->msg.data_len = recv->msg.data_len; 51237b1ce13SDoug Ambrisko break; 51337b1ce13SDoug Ambrisko } 514d72a0786SJohn Baldwin #endif 515d72a0786SJohn Baldwin return (0); 51637b1ce13SDoug Ambrisko } 51737b1ce13SDoug Ambrisko 518d72a0786SJohn Baldwin /* 519d72a0786SJohn Baldwin * Request management. 520d72a0786SJohn Baldwin */ 52137b1ce13SDoug Ambrisko 522c869aa71SJohn Baldwin static __inline void 523c869aa71SJohn Baldwin ipmi_init_request(struct ipmi_request *req, struct ipmi_device *dev, long msgid, 524c869aa71SJohn Baldwin uint8_t addr, uint8_t command, size_t requestlen, size_t replylen) 525d72a0786SJohn Baldwin { 526d72a0786SJohn Baldwin 527d72a0786SJohn Baldwin req->ir_owner = dev; 528d72a0786SJohn Baldwin req->ir_msgid = msgid; 529d72a0786SJohn Baldwin req->ir_addr = addr; 530d72a0786SJohn Baldwin req->ir_command = command; 531d72a0786SJohn Baldwin if (requestlen) { 532d72a0786SJohn Baldwin req->ir_request = (char *)&req[1]; 533d72a0786SJohn Baldwin req->ir_requestlen = requestlen; 534d72a0786SJohn Baldwin } 535d72a0786SJohn Baldwin if (replylen) { 536d72a0786SJohn Baldwin req->ir_reply = (char *)&req[1] + requestlen; 537d72a0786SJohn Baldwin req->ir_replybuflen = replylen; 538d72a0786SJohn Baldwin } 539c869aa71SJohn Baldwin } 540c869aa71SJohn Baldwin 541c869aa71SJohn Baldwin /* Allocate a new request with request and reply buffers. */ 542c869aa71SJohn Baldwin struct ipmi_request * 543c869aa71SJohn Baldwin ipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr, 544c869aa71SJohn Baldwin uint8_t command, size_t requestlen, size_t replylen) 545c869aa71SJohn Baldwin { 546c869aa71SJohn Baldwin struct ipmi_request *req; 547c869aa71SJohn Baldwin 548c869aa71SJohn Baldwin req = malloc(sizeof(struct ipmi_request) + requestlen + replylen, 549c869aa71SJohn Baldwin M_IPMI, M_WAITOK | M_ZERO); 550c869aa71SJohn Baldwin ipmi_init_request(req, dev, msgid, addr, command, requestlen, replylen); 551d72a0786SJohn Baldwin return (req); 55237b1ce13SDoug Ambrisko } 55337b1ce13SDoug Ambrisko 554d72a0786SJohn Baldwin /* Free a request no longer in use. */ 555d72a0786SJohn Baldwin void 556d72a0786SJohn Baldwin ipmi_free_request(struct ipmi_request *req) 557d72a0786SJohn Baldwin { 55837b1ce13SDoug Ambrisko 559d72a0786SJohn Baldwin free(req, M_IPMI); 56037b1ce13SDoug Ambrisko } 56137b1ce13SDoug Ambrisko 562d72a0786SJohn Baldwin /* Store a processed request on the appropriate completion queue. */ 563d72a0786SJohn Baldwin void 564d72a0786SJohn Baldwin ipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req) 565d72a0786SJohn Baldwin { 566d72a0786SJohn Baldwin struct ipmi_device *dev; 56737b1ce13SDoug Ambrisko 568d72a0786SJohn Baldwin IPMI_LOCK_ASSERT(sc); 569d72a0786SJohn Baldwin 570d72a0786SJohn Baldwin /* 571d72a0786SJohn Baldwin * Anonymous requests (from inside the driver) always have a 572d72a0786SJohn Baldwin * waiter that we awaken. 573d72a0786SJohn Baldwin */ 574d72a0786SJohn Baldwin if (req->ir_owner == NULL) 575d72a0786SJohn Baldwin wakeup(req); 576d72a0786SJohn Baldwin else { 577d72a0786SJohn Baldwin dev = req->ir_owner; 578d72a0786SJohn Baldwin TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link); 579d72a0786SJohn Baldwin selwakeup(&dev->ipmi_select); 580d72a0786SJohn Baldwin if (dev->ipmi_closing) 581d72a0786SJohn Baldwin wakeup(&dev->ipmi_requests); 582d72a0786SJohn Baldwin } 58337b1ce13SDoug Ambrisko } 58437b1ce13SDoug Ambrisko 585c869aa71SJohn Baldwin /* Perform an internal driver request. */ 58637b1ce13SDoug Ambrisko int 587d72a0786SJohn Baldwin ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, 588d72a0786SJohn Baldwin int timo) 589d72a0786SJohn Baldwin { 59037b1ce13SDoug Ambrisko 591c869aa71SJohn Baldwin return (sc->ipmi_driver_request(sc, req, timo)); 59237b1ce13SDoug Ambrisko } 59337b1ce13SDoug Ambrisko 594d72a0786SJohn Baldwin /* 595d72a0786SJohn Baldwin * Helper routine for polled system interfaces that use 596d72a0786SJohn Baldwin * ipmi_polled_enqueue_request() to queue requests. This request 597d72a0786SJohn Baldwin * waits until there is a pending request and then returns the first 598d72a0786SJohn Baldwin * request. If the driver is shutting down, it returns NULL. 599d72a0786SJohn Baldwin */ 600d72a0786SJohn Baldwin struct ipmi_request * 601d72a0786SJohn Baldwin ipmi_dequeue_request(struct ipmi_softc *sc) 602d72a0786SJohn Baldwin { 603d72a0786SJohn Baldwin struct ipmi_request *req; 60437b1ce13SDoug Ambrisko 605d72a0786SJohn Baldwin IPMI_LOCK_ASSERT(sc); 60637b1ce13SDoug Ambrisko 607d72a0786SJohn Baldwin while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests)) 608c869aa71SJohn Baldwin cv_wait(&sc->ipmi_request_added, &sc->ipmi_requests_lock); 609d72a0786SJohn Baldwin if (sc->ipmi_detaching) 610d72a0786SJohn Baldwin return (NULL); 61137b1ce13SDoug Ambrisko 612d72a0786SJohn Baldwin req = TAILQ_FIRST(&sc->ipmi_pending_requests); 613d72a0786SJohn Baldwin TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); 614d72a0786SJohn Baldwin return (req); 61537b1ce13SDoug Ambrisko } 61637b1ce13SDoug Ambrisko 617d72a0786SJohn Baldwin /* Default implementation of ipmi_enqueue_request() for polled interfaces. */ 618d72a0786SJohn Baldwin int 619d72a0786SJohn Baldwin ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req) 620d72a0786SJohn Baldwin { 62137b1ce13SDoug Ambrisko 6225283d39bSJohn Baldwin IPMI_LOCK_ASSERT(sc); 6235283d39bSJohn Baldwin 624d72a0786SJohn Baldwin TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link); 625d72a0786SJohn Baldwin cv_signal(&sc->ipmi_request_added); 626d72a0786SJohn Baldwin return (0); 62737b1ce13SDoug Ambrisko } 62837b1ce13SDoug Ambrisko 62937b1ce13SDoug Ambrisko /* 63037b1ce13SDoug Ambrisko * Watchdog event handler. 63137b1ce13SDoug Ambrisko */ 63237b1ce13SDoug Ambrisko 633a46a1e76SRuslan Ermilov static int 634ea2ef993SAlexander Motin ipmi_reset_watchdog(struct ipmi_softc *sc) 635ea2ef993SAlexander Motin { 636ea2ef993SAlexander Motin struct ipmi_request *req; 637ea2ef993SAlexander Motin int error; 638ea2ef993SAlexander Motin 639ea2ef993SAlexander Motin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 640ea2ef993SAlexander Motin IPMI_RESET_WDOG, 0, 0); 641ea2ef993SAlexander Motin error = ipmi_submit_driver_request(sc, req, 0); 642ea2ef993SAlexander Motin if (error) 643ea2ef993SAlexander Motin device_printf(sc->ipmi_dev, "Failed to reset watchdog\n"); 644ea2ef993SAlexander Motin return (error); 645ea2ef993SAlexander Motin } 646ea2ef993SAlexander Motin 647ea2ef993SAlexander Motin static int 648a46a1e76SRuslan Ermilov ipmi_set_watchdog(struct ipmi_softc *sc, unsigned int sec) 649d72a0786SJohn Baldwin { 650d72a0786SJohn Baldwin struct ipmi_request *req; 651d72a0786SJohn Baldwin int error; 65237b1ce13SDoug Ambrisko 653a46a1e76SRuslan Ermilov if (sec > 0xffff / 10) 654a46a1e76SRuslan Ermilov return (EINVAL); 655a46a1e76SRuslan Ermilov 656c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 657d72a0786SJohn Baldwin IPMI_SET_WDOG, 6, 0); 65837b1ce13SDoug Ambrisko if (sec) { 659d72a0786SJohn Baldwin req->ir_request[0] = IPMI_SET_WD_TIMER_DONT_STOP 66037b1ce13SDoug Ambrisko | IPMI_SET_WD_TIMER_SMS_OS; 66114d00450SWarner Losh req->ir_request[1] = (wd_timer_actions & 0xff); 66214d00450SWarner Losh req->ir_request[2] = (wd_pretimeout_countdown & 0xff); 663d72a0786SJohn Baldwin req->ir_request[3] = 0; /* Timer use */ 664d72a0786SJohn Baldwin req->ir_request[4] = (sec * 10) & 0xff; 665a46a1e76SRuslan Ermilov req->ir_request[5] = (sec * 10) >> 8; 66637b1ce13SDoug Ambrisko } else { 667d72a0786SJohn Baldwin req->ir_request[0] = IPMI_SET_WD_TIMER_SMS_OS; 668d72a0786SJohn Baldwin req->ir_request[1] = 0; 669d72a0786SJohn Baldwin req->ir_request[2] = 0; 670d72a0786SJohn Baldwin req->ir_request[3] = 0; /* Timer use */ 671d72a0786SJohn Baldwin req->ir_request[4] = 0; 672d72a0786SJohn Baldwin req->ir_request[5] = 0; 67337b1ce13SDoug Ambrisko } 674d72a0786SJohn Baldwin error = ipmi_submit_driver_request(sc, req, 0); 675d72a0786SJohn Baldwin if (error) 676d72a0786SJohn Baldwin device_printf(sc->ipmi_dev, "Failed to set watchdog\n"); 677a46a1e76SRuslan Ermilov return (error); 67837b1ce13SDoug Ambrisko } 67937b1ce13SDoug Ambrisko 68037b1ce13SDoug Ambrisko static void 68137b1ce13SDoug Ambrisko ipmi_wd_event(void *arg, unsigned int cmd, int *error) 68237b1ce13SDoug Ambrisko { 68337b1ce13SDoug Ambrisko struct ipmi_softc *sc = arg; 68437b1ce13SDoug Ambrisko unsigned int timeout; 685a46a1e76SRuslan Ermilov int e; 68637b1ce13SDoug Ambrisko 68714d00450SWarner Losh /* Ignore requests while disabled. */ 68814d00450SWarner Losh if (!on) 689a9b3c1bfSGleb Smirnoff return; 690a9b3c1bfSGleb Smirnoff 69114d00450SWarner Losh /* 69214d00450SWarner Losh * To prevent infinite hangs, we don't let anyone pat or change 69314d00450SWarner Losh * the watchdog when we're shutting down. (See ipmi_shutdown_event().) 69414d00450SWarner Losh * However, we do want to keep patting the watchdog while we are doing 69514d00450SWarner Losh * a coredump. 69614d00450SWarner Losh */ 69714d00450SWarner Losh if (wd_in_shutdown) { 69814d00450SWarner Losh if (dumping && sc->ipmi_watchdog_active) 69914d00450SWarner Losh ipmi_reset_watchdog(sc); 70014d00450SWarner Losh return; 70114d00450SWarner Losh } 70214d00450SWarner Losh 70337b1ce13SDoug Ambrisko cmd &= WD_INTERVAL; 7049079fff5SNick Hibma if (cmd > 0 && cmd <= 63) { 705a46a1e76SRuslan Ermilov timeout = ((uint64_t)1 << cmd) / 1000000000; 706a46a1e76SRuslan Ermilov if (timeout == 0) 707a46a1e76SRuslan Ermilov timeout = 1; 70814d00450SWarner Losh if (timeout != sc->ipmi_watchdog_active || 70914d00450SWarner Losh wd_timer_actions != sc->ipmi_watchdog_actions || 71014d00450SWarner Losh wd_pretimeout_countdown != sc->ipmi_watchdog_pretimeout) { 711a46a1e76SRuslan Ermilov e = ipmi_set_watchdog(sc, timeout); 7121710852eSJohn Baldwin if (e == 0) { 713ea2ef993SAlexander Motin sc->ipmi_watchdog_active = timeout; 71414d00450SWarner Losh sc->ipmi_watchdog_actions = wd_timer_actions; 71514d00450SWarner Losh sc->ipmi_watchdog_pretimeout = wd_pretimeout_countdown; 716ea2ef993SAlexander Motin } else { 717a46a1e76SRuslan Ermilov (void)ipmi_set_watchdog(sc, 0); 718ea2ef993SAlexander Motin sc->ipmi_watchdog_active = 0; 71914d00450SWarner Losh sc->ipmi_watchdog_actions = 0; 72014d00450SWarner Losh sc->ipmi_watchdog_pretimeout = 0; 721ea2ef993SAlexander Motin } 722ea2ef993SAlexander Motin } 723ea2ef993SAlexander Motin if (sc->ipmi_watchdog_active != 0) { 724ea2ef993SAlexander Motin e = ipmi_reset_watchdog(sc); 725ea2ef993SAlexander Motin if (e == 0) { 726ea2ef993SAlexander Motin *error = 0; 727ea2ef993SAlexander Motin } else { 728ea2ef993SAlexander Motin (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 } 7341710852eSJohn Baldwin } else if (atomic_readandclear_int(&sc->ipmi_watchdog_active) != 0) { 73514d00450SWarner Losh sc->ipmi_watchdog_actions = 0; 73614d00450SWarner Losh sc->ipmi_watchdog_pretimeout = 0; 73714d00450SWarner Losh 738a46a1e76SRuslan Ermilov e = ipmi_set_watchdog(sc, 0); 739a46a1e76SRuslan Ermilov if (e != 0 && cmd == 0) 740a46a1e76SRuslan Ermilov *error = EOPNOTSUPP; 7419079fff5SNick Hibma } 74237b1ce13SDoug Ambrisko } 74337b1ce13SDoug Ambrisko 744d72a0786SJohn Baldwin static void 74514d00450SWarner Losh ipmi_shutdown_event(void *arg, unsigned int cmd, int *error) 74614d00450SWarner Losh { 74714d00450SWarner Losh struct ipmi_softc *sc = arg; 74814d00450SWarner Losh 74914d00450SWarner Losh /* Ignore event if disabled. */ 75014d00450SWarner Losh if (!on) 75114d00450SWarner Losh return; 75214d00450SWarner Losh 75314d00450SWarner Losh /* 75414d00450SWarner Losh * Positive wd_shutdown_countdown value will re-arm watchdog; 75514d00450SWarner Losh * Zero value in wd_shutdown_countdown will disable watchdog; 75614d00450SWarner Losh * Negative value in wd_shutdown_countdown will keep existing state; 75714d00450SWarner Losh * 75814d00450SWarner Losh * Revert to using a power cycle to ensure that the watchdog will 75914d00450SWarner Losh * do something useful here. Having the watchdog send an NMI 76014d00450SWarner Losh * instead is useless during shutdown, and might be ignored if an 76114d00450SWarner Losh * NMI already triggered. 76214d00450SWarner Losh */ 76314d00450SWarner Losh 76414d00450SWarner Losh wd_in_shutdown = true; 76514d00450SWarner Losh if (wd_shutdown_countdown == 0) { 76614d00450SWarner Losh /* disable watchdog */ 76714d00450SWarner Losh ipmi_set_watchdog(sc, 0); 76814d00450SWarner Losh sc->ipmi_watchdog_active = 0; 76914d00450SWarner Losh } else if (wd_shutdown_countdown > 0) { 77014d00450SWarner Losh /* set desired action and time, and, reset watchdog */ 77114d00450SWarner Losh wd_timer_actions = IPMI_SET_WD_ACTION_POWER_CYCLE; 77214d00450SWarner Losh ipmi_set_watchdog(sc, wd_shutdown_countdown); 77314d00450SWarner Losh sc->ipmi_watchdog_active = wd_shutdown_countdown; 77414d00450SWarner Losh ipmi_reset_watchdog(sc); 77514d00450SWarner Losh } 77614d00450SWarner Losh } 77714d00450SWarner Losh 77814d00450SWarner Losh static void 7791170c2feSWarner Losh ipmi_power_cycle(void *arg, int howto) 7801170c2feSWarner Losh { 7811170c2feSWarner Losh struct ipmi_softc *sc = arg; 7821170c2feSWarner Losh struct ipmi_request *req; 7831170c2feSWarner Losh 7841170c2feSWarner Losh /* 7851170c2feSWarner Losh * Ignore everything except power cycling requests 7861170c2feSWarner Losh */ 7871170c2feSWarner Losh if ((howto & RB_POWERCYCLE) == 0) 7881170c2feSWarner Losh return; 7891170c2feSWarner Losh 7901170c2feSWarner Losh device_printf(sc->ipmi_dev, "Power cycling using IPMI\n"); 7911170c2feSWarner Losh 7921170c2feSWarner Losh /* 7931170c2feSWarner Losh * Send a CHASSIS_CONTROL command to the CHASSIS device, subcommand 2 7941170c2feSWarner Losh * as described in IPMI v2.0 spec section 28.3. 7951170c2feSWarner Losh */ 7961170c2feSWarner Losh IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_CHASSIS_REQUEST, 0), 7971170c2feSWarner Losh IPMI_CHASSIS_CONTROL, 1, 0); 7981170c2feSWarner Losh req->ir_request[0] = IPMI_CC_POWER_CYCLE; 7991170c2feSWarner Losh 8001170c2feSWarner Losh ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); 8011170c2feSWarner Losh 8021170c2feSWarner Losh if (req->ir_error != 0 || req->ir_compcode != 0) { 8031170c2feSWarner Losh device_printf(sc->ipmi_dev, "Power cycling via IPMI failed code %#x %#x\n", 8041170c2feSWarner Losh req->ir_error, req->ir_compcode); 8051170c2feSWarner Losh return; 8061170c2feSWarner Losh } 8071170c2feSWarner Losh 8081170c2feSWarner Losh /* 80916f0063eSWarner Losh * BMCs are notoriously slow, give it cyle_wait seconds for the power 8101170c2feSWarner Losh * down leg of the power cycle. If that fails, fallback to the next 8111170c2feSWarner Losh * hanlder in the shutdown_final chain and/or the platform failsafe. 8121170c2feSWarner Losh */ 81316f0063eSWarner Losh DELAY(cycle_wait * 1000 * 1000); 8141170c2feSWarner Losh device_printf(sc->ipmi_dev, "Power cycling via IPMI timed out\n"); 8151170c2feSWarner Losh } 8161170c2feSWarner Losh 8171170c2feSWarner Losh static void 818d72a0786SJohn Baldwin ipmi_startup(void *arg) 819d72a0786SJohn Baldwin { 820d72a0786SJohn Baldwin struct ipmi_softc *sc = arg; 821d72a0786SJohn Baldwin struct ipmi_request *req; 822d72a0786SJohn Baldwin device_t dev; 823d72a0786SJohn Baldwin int error, i; 824d72a0786SJohn Baldwin 825d72a0786SJohn Baldwin config_intrhook_disestablish(&sc->ipmi_ich); 826d72a0786SJohn Baldwin dev = sc->ipmi_dev; 827d72a0786SJohn Baldwin 828d72a0786SJohn Baldwin /* Initialize interface-independent state. */ 829c869aa71SJohn Baldwin mtx_init(&sc->ipmi_requests_lock, "ipmi requests", NULL, MTX_DEF); 830c869aa71SJohn Baldwin mtx_init(&sc->ipmi_io_lock, "ipmi io", NULL, MTX_DEF); 831d72a0786SJohn Baldwin cv_init(&sc->ipmi_request_added, "ipmireq"); 832d72a0786SJohn Baldwin TAILQ_INIT(&sc->ipmi_pending_requests); 833d72a0786SJohn Baldwin 834d72a0786SJohn Baldwin /* Initialize interface-dependent state. */ 835d72a0786SJohn Baldwin error = sc->ipmi_startup(sc); 836d72a0786SJohn Baldwin if (error) { 837d72a0786SJohn Baldwin device_printf(dev, "Failed to initialize interface: %d\n", 838d72a0786SJohn Baldwin error); 839d72a0786SJohn Baldwin return; 840d72a0786SJohn Baldwin } 841d72a0786SJohn Baldwin 842d72a0786SJohn Baldwin /* Send a GET_DEVICE_ID request. */ 843c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 844d72a0786SJohn Baldwin IPMI_GET_DEVICE_ID, 0, 15); 845d72a0786SJohn Baldwin 846d72a0786SJohn Baldwin error = ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); 847d72a0786SJohn Baldwin if (error == EWOULDBLOCK) { 848d72a0786SJohn Baldwin device_printf(dev, "Timed out waiting for GET_DEVICE_ID\n"); 849d72a0786SJohn Baldwin return; 850d72a0786SJohn Baldwin } else if (error) { 851d72a0786SJohn Baldwin device_printf(dev, "Failed GET_DEVICE_ID: %d\n", error); 852d72a0786SJohn Baldwin return; 853d72a0786SJohn Baldwin } else if (req->ir_compcode != 0) { 854d72a0786SJohn Baldwin device_printf(dev, 855d72a0786SJohn Baldwin "Bad completion code for GET_DEVICE_ID: %d\n", 856d72a0786SJohn Baldwin req->ir_compcode); 857d72a0786SJohn Baldwin return; 858d72a0786SJohn Baldwin } else if (req->ir_replylen < 5) { 859d72a0786SJohn Baldwin device_printf(dev, "Short reply for GET_DEVICE_ID: %d\n", 860d72a0786SJohn Baldwin req->ir_replylen); 861d72a0786SJohn Baldwin return; 862d72a0786SJohn Baldwin } 863d72a0786SJohn Baldwin 86414689886SRuslan Ermilov device_printf(dev, "IPMI device rev. %d, firmware rev. %d.%d%d, " 8651170c2feSWarner Losh "version %d.%d, device support mask %#x\n", 866d72a0786SJohn Baldwin req->ir_reply[1] & 0x0f, 86714689886SRuslan Ermilov req->ir_reply[2] & 0x7f, req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f, 8681170c2feSWarner Losh req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4, req->ir_reply[5]); 8691170c2feSWarner Losh 8701170c2feSWarner Losh sc->ipmi_dev_support = req->ir_reply[5]; 871d72a0786SJohn Baldwin 872c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 873d72a0786SJohn Baldwin IPMI_CLEAR_FLAGS, 1, 0); 874d72a0786SJohn Baldwin 875d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req, 0); 876d72a0786SJohn Baldwin 877d72a0786SJohn Baldwin /* XXX: Magic numbers */ 878d72a0786SJohn Baldwin if (req->ir_compcode == 0xc0) { 879d72a0786SJohn Baldwin device_printf(dev, "Clear flags is busy\n"); 880d72a0786SJohn Baldwin } 881d72a0786SJohn Baldwin if (req->ir_compcode == 0xc1) { 882d72a0786SJohn Baldwin device_printf(dev, "Clear flags illegal\n"); 883d72a0786SJohn Baldwin } 884d72a0786SJohn Baldwin 885d72a0786SJohn Baldwin for (i = 0; i < 8; i++) { 886c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 887d72a0786SJohn Baldwin IPMI_GET_CHANNEL_INFO, 1, 0); 888d72a0786SJohn Baldwin req->ir_request[0] = i; 889d72a0786SJohn Baldwin 890d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req, 0); 891d72a0786SJohn Baldwin 892c869aa71SJohn Baldwin if (req->ir_compcode != 0) 893d72a0786SJohn Baldwin break; 894d72a0786SJohn Baldwin } 895d72a0786SJohn Baldwin device_printf(dev, "Number of channels %d\n", i); 896d72a0786SJohn Baldwin 8979662eef5SJohn Baldwin /* 8989662eef5SJohn Baldwin * Probe for watchdog, but only for backends which support 8999662eef5SJohn Baldwin * polled driver requests. 9009662eef5SJohn Baldwin */ 9019662eef5SJohn Baldwin if (sc->ipmi_driver_requests_polled) { 902c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 903d72a0786SJohn Baldwin IPMI_GET_WDOG, 0, 0); 904d72a0786SJohn Baldwin 905d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req, 0); 906d72a0786SJohn Baldwin 907d72a0786SJohn Baldwin if (req->ir_compcode == 0x00) { 908d72a0786SJohn Baldwin device_printf(dev, "Attached watchdog\n"); 909d72a0786SJohn Baldwin /* register the watchdog event handler */ 9109662eef5SJohn Baldwin sc->ipmi_watchdog_tag = EVENTHANDLER_REGISTER( 9119662eef5SJohn Baldwin watchdog_list, ipmi_wd_event, sc, 0); 91214d00450SWarner Losh sc->ipmi_shutdown_tag = EVENTHANDLER_REGISTER( 91314d00450SWarner Losh shutdown_pre_sync, ipmi_shutdown_event, 91414d00450SWarner Losh sc, 0); 9159662eef5SJohn Baldwin } 916d72a0786SJohn Baldwin } 917d72a0786SJohn Baldwin 918943bebd2SJohn Baldwin sc->ipmi_cdev = make_dev(&ipmi_cdevsw, device_get_unit(dev), 919d72a0786SJohn Baldwin UID_ROOT, GID_OPERATOR, 0660, "ipmi%d", device_get_unit(dev)); 920943bebd2SJohn Baldwin if (sc->ipmi_cdev == NULL) { 921d72a0786SJohn Baldwin device_printf(dev, "Failed to create cdev\n"); 922d72a0786SJohn Baldwin return; 923d72a0786SJohn Baldwin } 924943bebd2SJohn Baldwin sc->ipmi_cdev->si_drv1 = sc; 9251170c2feSWarner Losh 9261170c2feSWarner Losh /* 92714d00450SWarner Losh * Set initial watchdog state. If desired, set an initial 92814d00450SWarner Losh * watchdog on startup. Or, if the watchdog device is 92914d00450SWarner Losh * disabled, clear any existing watchdog. 93014d00450SWarner Losh */ 93114d00450SWarner Losh if (on && wd_startup_countdown > 0) { 93214d00450SWarner Losh wd_timer_actions = IPMI_SET_WD_ACTION_POWER_CYCLE; 93314d00450SWarner Losh if (ipmi_set_watchdog(sc, wd_startup_countdown) == 0 && 93414d00450SWarner Losh ipmi_reset_watchdog(sc) == 0) { 93514d00450SWarner Losh sc->ipmi_watchdog_active = wd_startup_countdown; 93614d00450SWarner Losh sc->ipmi_watchdog_actions = wd_timer_actions; 93714d00450SWarner Losh sc->ipmi_watchdog_pretimeout = wd_pretimeout_countdown; 93814d00450SWarner Losh } else 93914d00450SWarner Losh (void)ipmi_set_watchdog(sc, 0); 94014d00450SWarner Losh ipmi_reset_watchdog(sc); 94114d00450SWarner Losh } else if (!on) 94214d00450SWarner Losh (void)ipmi_set_watchdog(sc, 0); 94314d00450SWarner Losh /* 94426649bb5SConrad Meyer * Power cycle the system off using IPMI. We use last - 2 since we don't 9451170c2feSWarner Losh * handle all the other kinds of reboots. We'll let others handle them. 9461170c2feSWarner Losh * We only try to do this if the BMC supports the Chassis device. 9471170c2feSWarner Losh */ 9481170c2feSWarner Losh if (sc->ipmi_dev_support & IPMI_ADS_CHASSIS) { 9491170c2feSWarner Losh device_printf(dev, "Establishing power cycle handler\n"); 9501170c2feSWarner Losh sc->ipmi_power_cycle_tag = EVENTHANDLER_REGISTER(shutdown_final, 95126649bb5SConrad Meyer ipmi_power_cycle, sc, SHUTDOWN_PRI_LAST - 2); 9521170c2feSWarner Losh } 953d72a0786SJohn Baldwin } 954d72a0786SJohn Baldwin 95537b1ce13SDoug Ambrisko int 95637b1ce13SDoug Ambrisko ipmi_attach(device_t dev) 95737b1ce13SDoug Ambrisko { 95837b1ce13SDoug Ambrisko struct ipmi_softc *sc = device_get_softc(dev); 959d72a0786SJohn Baldwin int error; 96037b1ce13SDoug Ambrisko 961d72a0786SJohn Baldwin if (sc->ipmi_irq_res != NULL && sc->ipmi_intr != NULL) { 962d72a0786SJohn Baldwin error = bus_setup_intr(dev, sc->ipmi_irq_res, INTR_TYPE_MISC, 963ef544f63SPaolo Pisati NULL, sc->ipmi_intr, sc, &sc->ipmi_irq); 964d72a0786SJohn Baldwin if (error) { 965d72a0786SJohn Baldwin device_printf(dev, "can't set up interrupt\n"); 966d72a0786SJohn Baldwin return (error); 96737b1ce13SDoug Ambrisko } 96837b1ce13SDoug Ambrisko } 96937b1ce13SDoug Ambrisko 970d72a0786SJohn Baldwin bzero(&sc->ipmi_ich, sizeof(struct intr_config_hook)); 971d72a0786SJohn Baldwin sc->ipmi_ich.ich_func = ipmi_startup; 972d72a0786SJohn Baldwin sc->ipmi_ich.ich_arg = sc; 973d72a0786SJohn Baldwin if (config_intrhook_establish(&sc->ipmi_ich) != 0) { 974d72a0786SJohn Baldwin device_printf(dev, "can't establish configuration hook\n"); 975d72a0786SJohn Baldwin return (ENOMEM); 97637b1ce13SDoug Ambrisko } 97737b1ce13SDoug Ambrisko 97837b1ce13SDoug Ambrisko ipmi_attached = 1; 979d72a0786SJohn Baldwin return (0); 98037b1ce13SDoug Ambrisko } 98137b1ce13SDoug Ambrisko 98237b1ce13SDoug Ambrisko int 98337b1ce13SDoug Ambrisko ipmi_detach(device_t dev) 98437b1ce13SDoug Ambrisko { 98537b1ce13SDoug Ambrisko struct ipmi_softc *sc; 98637b1ce13SDoug Ambrisko 98737b1ce13SDoug Ambrisko sc = device_get_softc(dev); 988d72a0786SJohn Baldwin 989d72a0786SJohn Baldwin /* Fail if there are any open handles. */ 990d72a0786SJohn Baldwin IPMI_LOCK(sc); 991943bebd2SJohn Baldwin if (sc->ipmi_opened) { 992d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 993d72a0786SJohn Baldwin return (EBUSY); 994d72a0786SJohn Baldwin } 995d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 996943bebd2SJohn Baldwin if (sc->ipmi_cdev) 997943bebd2SJohn Baldwin destroy_dev(sc->ipmi_cdev); 998d72a0786SJohn Baldwin 999d72a0786SJohn Baldwin /* Detach from watchdog handling and turn off watchdog. */ 100014d00450SWarner Losh if (sc->ipmi_shutdown_tag) 100114d00450SWarner Losh EVENTHANDLER_DEREGISTER(shutdown_pre_sync, 100214d00450SWarner Losh sc->ipmi_shutdown_tag); 1003d72a0786SJohn Baldwin if (sc->ipmi_watchdog_tag) { 1004d72a0786SJohn Baldwin EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_watchdog_tag); 1005d72a0786SJohn Baldwin ipmi_set_watchdog(sc, 0); 1006d72a0786SJohn Baldwin } 1007d72a0786SJohn Baldwin 10081170c2feSWarner Losh /* Detach from shutdown handling for power cycle reboot */ 10091170c2feSWarner Losh if (sc->ipmi_power_cycle_tag) 10101170c2feSWarner Losh EVENTHANDLER_DEREGISTER(shutdown_final, sc->ipmi_power_cycle_tag); 10111170c2feSWarner Losh 1012d72a0786SJohn Baldwin /* XXX: should use shutdown callout I think. */ 1013d72a0786SJohn Baldwin /* If the backend uses a kthread, shut it down. */ 1014d72a0786SJohn Baldwin IPMI_LOCK(sc); 1015d72a0786SJohn Baldwin sc->ipmi_detaching = 1; 1016d72a0786SJohn Baldwin if (sc->ipmi_kthread) { 1017d72a0786SJohn Baldwin cv_broadcast(&sc->ipmi_request_added); 1018c869aa71SJohn Baldwin msleep(sc->ipmi_kthread, &sc->ipmi_requests_lock, 0, 1019c869aa71SJohn Baldwin "ipmi_wait", 0); 1020d72a0786SJohn Baldwin } 1021d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 1022d72a0786SJohn Baldwin if (sc->ipmi_irq) 1023d72a0786SJohn Baldwin bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); 1024d72a0786SJohn Baldwin 1025d72a0786SJohn Baldwin ipmi_release_resources(dev); 1026c869aa71SJohn Baldwin mtx_destroy(&sc->ipmi_io_lock); 1027c869aa71SJohn Baldwin mtx_destroy(&sc->ipmi_requests_lock); 1028d72a0786SJohn Baldwin return (0); 1029d72a0786SJohn Baldwin } 1030d72a0786SJohn Baldwin 1031d72a0786SJohn Baldwin void 1032d72a0786SJohn Baldwin ipmi_release_resources(device_t dev) 1033d72a0786SJohn Baldwin { 1034d72a0786SJohn Baldwin struct ipmi_softc *sc; 1035d72a0786SJohn Baldwin int i; 1036d72a0786SJohn Baldwin 1037d72a0786SJohn Baldwin sc = device_get_softc(dev); 1038d72a0786SJohn Baldwin if (sc->ipmi_irq) 1039d72a0786SJohn Baldwin bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); 1040d72a0786SJohn Baldwin if (sc->ipmi_irq_res) 1041d72a0786SJohn Baldwin bus_release_resource(dev, SYS_RES_IRQ, sc->ipmi_irq_rid, 1042d72a0786SJohn Baldwin sc->ipmi_irq_res); 1043d72a0786SJohn Baldwin for (i = 0; i < MAX_RES; i++) 1044d72a0786SJohn Baldwin if (sc->ipmi_io_res[i]) 1045d72a0786SJohn Baldwin bus_release_resource(dev, sc->ipmi_io_type, 1046d72a0786SJohn Baldwin sc->ipmi_io_rid + i, sc->ipmi_io_res[i]); 1047d72a0786SJohn Baldwin } 1048d72a0786SJohn Baldwin 1049d72a0786SJohn Baldwin devclass_t ipmi_devclass; 1050d72a0786SJohn Baldwin 1051d72a0786SJohn Baldwin /* XXX: Why? */ 1052d72a0786SJohn Baldwin static void 1053d72a0786SJohn Baldwin ipmi_unload(void *arg) 1054d72a0786SJohn Baldwin { 1055d72a0786SJohn Baldwin device_t * devs; 1056d72a0786SJohn Baldwin int count; 1057d72a0786SJohn Baldwin int i; 1058d72a0786SJohn Baldwin 10593991dbf3SDoug Ambrisko if (ipmi_devclass == NULL) 10603991dbf3SDoug Ambrisko return; 1061bec0c98eSJohn Baldwin if (devclass_get_devices(ipmi_devclass, &devs, &count) != 0) 1062bec0c98eSJohn Baldwin return; 1063d72a0786SJohn Baldwin for (i = 0; i < count; i++) 1064d72a0786SJohn Baldwin device_delete_child(device_get_parent(devs[i]), devs[i]); 1065bec0c98eSJohn Baldwin free(devs, M_TEMP); 1066d72a0786SJohn Baldwin } 1067d72a0786SJohn Baldwin SYSUNINIT(ipmi_unload, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipmi_unload, NULL); 1068d72a0786SJohn Baldwin 106919d61f3fSDoug Ambrisko #ifdef IMPI_DEBUG 107037b1ce13SDoug Ambrisko static void 1071d72a0786SJohn Baldwin dump_buf(u_char *data, int len) 1072d72a0786SJohn Baldwin { 107337b1ce13SDoug Ambrisko char buf[20]; 107437b1ce13SDoug Ambrisko char line[1024]; 107537b1ce13SDoug Ambrisko char temp[30]; 107637b1ce13SDoug Ambrisko int count = 0; 107737b1ce13SDoug Ambrisko int i=0; 107837b1ce13SDoug Ambrisko 107937b1ce13SDoug Ambrisko printf("Address %p len %d\n", data, len); 108037b1ce13SDoug Ambrisko if (len > 256) 108137b1ce13SDoug Ambrisko len = 256; 108237b1ce13SDoug Ambrisko line[0] = '\000'; 108337b1ce13SDoug Ambrisko for (; len > 0; len--, data++) { 108437b1ce13SDoug Ambrisko sprintf(temp, "%02x ", *data); 108537b1ce13SDoug Ambrisko strcat(line, temp); 108637b1ce13SDoug Ambrisko if (*data >= ' ' && *data <= '~') 108737b1ce13SDoug Ambrisko buf[count] = *data; 108837b1ce13SDoug Ambrisko else if (*data >= 'A' && *data <= 'Z') 108937b1ce13SDoug Ambrisko buf[count] = *data; 109037b1ce13SDoug Ambrisko else 109137b1ce13SDoug Ambrisko buf[count] = '.'; 109237b1ce13SDoug Ambrisko if (++count == 16) { 109337b1ce13SDoug Ambrisko buf[count] = '\000'; 109437b1ce13SDoug Ambrisko count = 0; 109537b1ce13SDoug Ambrisko printf(" %3x %s %s\n", i, line, buf); 109637b1ce13SDoug Ambrisko i+=16; 109737b1ce13SDoug Ambrisko line[0] = '\000'; 109837b1ce13SDoug Ambrisko } 109937b1ce13SDoug Ambrisko } 110037b1ce13SDoug Ambrisko buf[count] = '\000'; 110137b1ce13SDoug Ambrisko 110237b1ce13SDoug Ambrisko for (; count != 16; count++) { 110337b1ce13SDoug Ambrisko strcat(line, " "); 110437b1ce13SDoug Ambrisko } 110537b1ce13SDoug Ambrisko printf(" %3x %s %s\n", i, line, buf); 110637b1ce13SDoug Ambrisko } 110737b1ce13SDoug Ambrisko #endif 1108