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> 3737b1ce13SDoug Ambrisko #include <sys/kernel.h> 3837b1ce13SDoug Ambrisko #include <sys/malloc.h> 3937b1ce13SDoug Ambrisko #include <sys/module.h> 40d72a0786SJohn Baldwin #include <sys/poll.h> 411170c2feSWarner Losh #include <sys/reboot.h> 4237b1ce13SDoug Ambrisko #include <sys/rman.h> 43d72a0786SJohn Baldwin #include <sys/selinfo.h> 4437b1ce13SDoug Ambrisko #include <sys/sysctl.h> 45d72a0786SJohn Baldwin #include <sys/watchdog.h> 4637b1ce13SDoug Ambrisko 4737b1ce13SDoug Ambrisko #ifdef LOCAL_MODULE 4837b1ce13SDoug Ambrisko #include <ipmi.h> 4937b1ce13SDoug Ambrisko #include <ipmivars.h> 5037b1ce13SDoug Ambrisko #else 5137b1ce13SDoug Ambrisko #include <sys/ipmi.h> 5237b1ce13SDoug Ambrisko #include <dev/ipmi/ipmivars.h> 5337b1ce13SDoug Ambrisko #endif 5437b1ce13SDoug Ambrisko 55c869aa71SJohn Baldwin /* 56c869aa71SJohn Baldwin * Driver request structures are allocated on the stack via alloca() to 57c869aa71SJohn Baldwin * avoid calling malloc(), especially for the watchdog handler. 58c869aa71SJohn Baldwin * To avoid too much stack growth, a previously allocated structure can 59c869aa71SJohn Baldwin * be reused via IPMI_INIT_DRIVER_REQUEST(), but the caller should ensure 60c869aa71SJohn Baldwin * that there is adequate reply/request space in the original allocation. 61c869aa71SJohn Baldwin */ 62c869aa71SJohn Baldwin #define IPMI_INIT_DRIVER_REQUEST(req, addr, cmd, reqlen, replylen) \ 63c869aa71SJohn Baldwin bzero((req), sizeof(struct ipmi_request)); \ 64c869aa71SJohn Baldwin ipmi_init_request((req), NULL, 0, (addr), (cmd), (reqlen), (replylen)) 65c869aa71SJohn Baldwin 66c869aa71SJohn Baldwin #define IPMI_ALLOC_DRIVER_REQUEST(req, addr, cmd, reqlen, replylen) \ 67c869aa71SJohn Baldwin (req) = __builtin_alloca(sizeof(struct ipmi_request) + \ 68c869aa71SJohn Baldwin (reqlen) + (replylen)); \ 69c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST((req), (addr), (cmd), (reqlen), \ 70c869aa71SJohn Baldwin (replylen)) 71c869aa71SJohn Baldwin 7237b1ce13SDoug Ambrisko #ifdef IPMB 7337b1ce13SDoug Ambrisko static int ipmi_ipmb_checksum(u_char, int); 7437b1ce13SDoug Ambrisko static int ipmi_ipmb_send_message(device_t, u_char, u_char, u_char, 7537b1ce13SDoug Ambrisko u_char, u_char, int) 7637b1ce13SDoug Ambrisko #endif 7737b1ce13SDoug Ambrisko 7837b1ce13SDoug Ambrisko static d_ioctl_t ipmi_ioctl; 7937b1ce13SDoug Ambrisko static d_poll_t ipmi_poll; 8037b1ce13SDoug Ambrisko static d_open_t ipmi_open; 81943bebd2SJohn Baldwin static void ipmi_dtor(void *arg); 8237b1ce13SDoug Ambrisko 8337b1ce13SDoug Ambrisko int ipmi_attached = 0; 8437b1ce13SDoug Ambrisko 8537b1ce13SDoug Ambrisko static int on = 1; 8614d00450SWarner Losh static bool wd_in_shutdown = false; 8714d00450SWarner Losh static int wd_timer_actions = IPMI_SET_WD_ACTION_POWER_CYCLE; 889ee3ea71SPeter Wemm static int wd_shutdown_countdown = 0; /* sec */ 89c154763dSWarner Losh static int wd_startup_countdown = 0; /* sec */ 9014d00450SWarner Losh static int wd_pretimeout_countdown = 120; /* sec */ 9116f0063eSWarner Losh static int cycle_wait = 10; /* sec */ 9214d00450SWarner Losh 936472ac3dSEd Schouten static SYSCTL_NODE(_hw, OID_AUTO, ipmi, CTLFLAG_RD, 0, 946472ac3dSEd Schouten "IPMI driver parameters"); 9514d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, on, CTLFLAG_RWTUN, 9637b1ce13SDoug Ambrisko &on, 0, ""); 9714d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_timer_actions, CTLFLAG_RW, 9814d00450SWarner Losh &wd_timer_actions, 0, 9914d00450SWarner Losh "IPMI watchdog timer actions (including pre-timeout interrupt)"); 10014d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_shutdown_countdown, CTLFLAG_RW, 10114d00450SWarner Losh &wd_shutdown_countdown, 0, 10214d00450SWarner Losh "IPMI watchdog countdown for shutdown (seconds)"); 10314d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_startup_countdown, CTLFLAG_RDTUN, 10414d00450SWarner Losh &wd_startup_countdown, 0, 10514d00450SWarner Losh "IPMI watchdog countdown initialized during startup (seconds)"); 10614d00450SWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, wd_pretimeout_countdown, CTLFLAG_RW, 10714d00450SWarner Losh &wd_pretimeout_countdown, 0, 10814d00450SWarner Losh "IPMI watchdog pre-timeout countdown (seconds)"); 10916f0063eSWarner Losh SYSCTL_INT(_hw_ipmi, OID_AUTO, cyle_wait, CTLFLAG_RWTUN, 11016f0063eSWarner Losh &cycle_wait, 0, 11116f0063eSWarner Losh "IPMI power cycle on reboot delay time (seconds)"); 11237b1ce13SDoug Ambrisko 11337b1ce13SDoug Ambrisko static struct cdevsw ipmi_cdevsw = { 11437b1ce13SDoug Ambrisko .d_version = D_VERSION, 11537b1ce13SDoug Ambrisko .d_open = ipmi_open, 11637b1ce13SDoug Ambrisko .d_ioctl = ipmi_ioctl, 11737b1ce13SDoug Ambrisko .d_poll = ipmi_poll, 11837b1ce13SDoug Ambrisko .d_name = "ipmi", 11937b1ce13SDoug Ambrisko }; 12037b1ce13SDoug Ambrisko 121d745c852SEd Schouten static MALLOC_DEFINE(M_IPMI, "ipmi", "ipmi"); 12237b1ce13SDoug Ambrisko 12337b1ce13SDoug Ambrisko static int 124d72a0786SJohn Baldwin ipmi_open(struct cdev *cdev, int flags, int fmt, struct thread *td) 12537b1ce13SDoug Ambrisko { 126d72a0786SJohn Baldwin struct ipmi_device *dev; 12737b1ce13SDoug Ambrisko struct ipmi_softc *sc; 128943bebd2SJohn Baldwin int error; 12937b1ce13SDoug Ambrisko 13037b1ce13SDoug Ambrisko if (!on) 131d72a0786SJohn Baldwin return (ENOENT); 13237b1ce13SDoug Ambrisko 133943bebd2SJohn Baldwin /* Initialize the per file descriptor data. */ 134943bebd2SJohn Baldwin dev = malloc(sizeof(struct ipmi_device), M_IPMI, M_WAITOK | M_ZERO); 135943bebd2SJohn Baldwin error = devfs_set_cdevpriv(dev, ipmi_dtor); 136943bebd2SJohn Baldwin if (error) { 137943bebd2SJohn Baldwin free(dev, M_IPMI); 138943bebd2SJohn Baldwin return (error); 13937b1ce13SDoug Ambrisko } 140943bebd2SJohn Baldwin 141943bebd2SJohn Baldwin sc = cdev->si_drv1; 142943bebd2SJohn Baldwin TAILQ_INIT(&dev->ipmi_completed_requests); 143943bebd2SJohn Baldwin dev->ipmi_address = IPMI_BMC_SLAVE_ADDR; 144943bebd2SJohn Baldwin dev->ipmi_lun = IPMI_BMC_SMS_LUN; 145943bebd2SJohn Baldwin dev->ipmi_softc = sc; 146943bebd2SJohn Baldwin IPMI_LOCK(sc); 147943bebd2SJohn Baldwin sc->ipmi_opened++; 148d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 14937b1ce13SDoug Ambrisko 150d72a0786SJohn Baldwin return (0); 15137b1ce13SDoug Ambrisko } 15237b1ce13SDoug Ambrisko 15337b1ce13SDoug Ambrisko static int 154d72a0786SJohn Baldwin ipmi_poll(struct cdev *cdev, int poll_events, struct thread *td) 15537b1ce13SDoug Ambrisko { 156d72a0786SJohn Baldwin struct ipmi_device *dev; 15737b1ce13SDoug Ambrisko struct ipmi_softc *sc; 15837b1ce13SDoug Ambrisko int revents = 0; 15937b1ce13SDoug Ambrisko 160943bebd2SJohn Baldwin if (devfs_get_cdevpriv((void **)&dev)) 161943bebd2SJohn Baldwin return (0); 16237b1ce13SDoug Ambrisko 163943bebd2SJohn Baldwin sc = cdev->si_drv1; 164d72a0786SJohn Baldwin IPMI_LOCK(sc); 16537b1ce13SDoug Ambrisko if (poll_events & (POLLIN | POLLRDNORM)) { 166d72a0786SJohn Baldwin if (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) 16737b1ce13SDoug Ambrisko revents |= poll_events & (POLLIN | POLLRDNORM); 168d72a0786SJohn Baldwin if (dev->ipmi_requests == 0) 16937b1ce13SDoug Ambrisko revents |= POLLERR; 17037b1ce13SDoug Ambrisko } 17137b1ce13SDoug Ambrisko 17237b1ce13SDoug Ambrisko if (revents == 0) { 17337b1ce13SDoug Ambrisko if (poll_events & (POLLIN | POLLRDNORM)) 174d72a0786SJohn Baldwin selrecord(td, &dev->ipmi_select); 175d72a0786SJohn Baldwin } 176d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 177d72a0786SJohn Baldwin 178d72a0786SJohn Baldwin return (revents); 17937b1ce13SDoug Ambrisko } 18037b1ce13SDoug Ambrisko 181d72a0786SJohn Baldwin static void 182d72a0786SJohn Baldwin ipmi_purge_completed_requests(struct ipmi_device *dev) 183d72a0786SJohn Baldwin { 184d72a0786SJohn Baldwin struct ipmi_request *req; 185d72a0786SJohn Baldwin 186d72a0786SJohn Baldwin while (!TAILQ_EMPTY(&dev->ipmi_completed_requests)) { 187d72a0786SJohn Baldwin req = TAILQ_FIRST(&dev->ipmi_completed_requests); 188d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, req, ir_link); 189d72a0786SJohn Baldwin dev->ipmi_requests--; 190d72a0786SJohn Baldwin ipmi_free_request(req); 191d72a0786SJohn Baldwin } 19237b1ce13SDoug Ambrisko } 19337b1ce13SDoug Ambrisko 194943bebd2SJohn Baldwin static void 195943bebd2SJohn Baldwin ipmi_dtor(void *arg) 19637b1ce13SDoug Ambrisko { 197d72a0786SJohn Baldwin struct ipmi_request *req, *nreq; 198d72a0786SJohn Baldwin struct ipmi_device *dev; 19937b1ce13SDoug Ambrisko struct ipmi_softc *sc; 20037b1ce13SDoug Ambrisko 201943bebd2SJohn Baldwin dev = arg; 202d72a0786SJohn Baldwin sc = dev->ipmi_softc; 20337b1ce13SDoug Ambrisko 204d72a0786SJohn Baldwin IPMI_LOCK(sc); 205d72a0786SJohn Baldwin if (dev->ipmi_requests) { 206d72a0786SJohn Baldwin /* Throw away any pending requests for this device. */ 207d72a0786SJohn Baldwin TAILQ_FOREACH_SAFE(req, &sc->ipmi_pending_requests, ir_link, 208d72a0786SJohn Baldwin nreq) { 209d72a0786SJohn Baldwin if (req->ir_owner == dev) { 210d72a0786SJohn Baldwin TAILQ_REMOVE(&sc->ipmi_pending_requests, req, 211d72a0786SJohn Baldwin ir_link); 212d72a0786SJohn Baldwin dev->ipmi_requests--; 213d72a0786SJohn Baldwin ipmi_free_request(req); 214d72a0786SJohn Baldwin } 215d72a0786SJohn Baldwin } 21637b1ce13SDoug Ambrisko 217d72a0786SJohn Baldwin /* Throw away any pending completed requests for this device. */ 218d72a0786SJohn Baldwin ipmi_purge_completed_requests(dev); 219d72a0786SJohn Baldwin 220d72a0786SJohn Baldwin /* 221d72a0786SJohn Baldwin * If we still have outstanding requests, they must be stuck 222d72a0786SJohn Baldwin * in an interface driver, so wait for those to drain. 223d72a0786SJohn Baldwin */ 224d72a0786SJohn Baldwin dev->ipmi_closing = 1; 225d72a0786SJohn Baldwin while (dev->ipmi_requests > 0) { 226c869aa71SJohn Baldwin msleep(&dev->ipmi_requests, &sc->ipmi_requests_lock, 227c869aa71SJohn Baldwin PWAIT, "ipmidrain", 0); 228d72a0786SJohn Baldwin ipmi_purge_completed_requests(dev); 229d72a0786SJohn Baldwin } 230d72a0786SJohn Baldwin } 231943bebd2SJohn Baldwin sc->ipmi_opened--; 232d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 233d72a0786SJohn Baldwin 234d72a0786SJohn Baldwin /* Cleanup. */ 235d72a0786SJohn Baldwin free(dev, M_IPMI); 23637b1ce13SDoug Ambrisko } 23737b1ce13SDoug Ambrisko 23837b1ce13SDoug Ambrisko #ifdef IPMB 23937b1ce13SDoug Ambrisko static int 24037b1ce13SDoug Ambrisko ipmi_ipmb_checksum(u_char *data, int len) 24137b1ce13SDoug Ambrisko { 24237b1ce13SDoug Ambrisko u_char sum = 0; 24337b1ce13SDoug Ambrisko 24437b1ce13SDoug Ambrisko for (; len; len--) { 24537b1ce13SDoug Ambrisko sum += *data++; 24637b1ce13SDoug Ambrisko } 247d72a0786SJohn Baldwin return (-sum); 24837b1ce13SDoug Ambrisko } 24937b1ce13SDoug Ambrisko 250d72a0786SJohn Baldwin /* XXX: Needs work */ 25137b1ce13SDoug Ambrisko static int 25237b1ce13SDoug Ambrisko ipmi_ipmb_send_message(device_t dev, u_char channel, u_char netfn, 25337b1ce13SDoug Ambrisko u_char command, u_char seq, u_char *data, int data_len) 25437b1ce13SDoug Ambrisko { 25537b1ce13SDoug Ambrisko struct ipmi_softc *sc = device_get_softc(dev); 256d72a0786SJohn Baldwin struct ipmi_request *req; 25737b1ce13SDoug Ambrisko u_char slave_addr = 0x52; 258d72a0786SJohn Baldwin int error; 25937b1ce13SDoug Ambrisko 260c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 261d72a0786SJohn Baldwin IPMI_SEND_MSG, data_len + 8, 0); 262d72a0786SJohn Baldwin req->ir_request[0] = channel; 263d72a0786SJohn Baldwin req->ir_request[1] = slave_addr; 264d72a0786SJohn Baldwin req->ir_request[2] = IPMI_ADDR(netfn, 0); 265d72a0786SJohn Baldwin req->ir_request[3] = ipmi_ipmb_checksum(&req->ir_request[1], 2); 266d72a0786SJohn Baldwin req->ir_request[4] = sc->ipmi_address; 267d72a0786SJohn Baldwin req->ir_request[5] = IPMI_ADDR(seq, sc->ipmi_lun); 268d72a0786SJohn Baldwin req->ir_request[6] = command; 26937b1ce13SDoug Ambrisko 270d72a0786SJohn Baldwin bcopy(data, &req->ir_request[7], data_len); 271d72a0786SJohn Baldwin temp[data_len + 7] = ipmi_ipmb_checksum(&req->ir_request[4], 272d72a0786SJohn Baldwin data_len + 3); 27337b1ce13SDoug Ambrisko 274d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req); 275d72a0786SJohn Baldwin error = req->ir_error; 27637b1ce13SDoug Ambrisko 277d72a0786SJohn Baldwin return (error); 27837b1ce13SDoug Ambrisko } 27937b1ce13SDoug Ambrisko 28037b1ce13SDoug Ambrisko static int 281d72a0786SJohn Baldwin ipmi_handle_attn(struct ipmi_softc *sc) 28237b1ce13SDoug Ambrisko { 283d72a0786SJohn Baldwin struct ipmi_request *req; 28437b1ce13SDoug Ambrisko int error; 28537b1ce13SDoug Ambrisko 28637b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "BMC has a message\n"); 287c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 288d72a0786SJohn Baldwin IPMI_GET_MSG_FLAGS, 0, 1); 28937b1ce13SDoug Ambrisko 290d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req); 291d72a0786SJohn Baldwin 292d72a0786SJohn Baldwin if (req->ir_error == 0 && req->ir_compcode == 0) { 293d72a0786SJohn Baldwin if (req->ir_reply[0] & IPMI_MSG_BUFFER_FULL) { 29437b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "message buffer full"); 29537b1ce13SDoug Ambrisko } 296d72a0786SJohn Baldwin if (req->ir_reply[0] & IPMI_WDT_PRE_TIMEOUT) { 29737b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, 29837b1ce13SDoug Ambrisko "watchdog about to go off"); 29937b1ce13SDoug Ambrisko } 300d72a0786SJohn Baldwin if (req->ir_reply[0] & IPMI_MSG_AVAILABLE) { 301c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, 302d72a0786SJohn Baldwin IPMI_ADDR(IPMI_APP_REQUEST, 0), IPMI_GET_MSG, 0, 303d72a0786SJohn Baldwin 16); 30437b1ce13SDoug Ambrisko 30537b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "throw out message "); 30637b1ce13SDoug Ambrisko dump_buf(temp, 16); 30737b1ce13SDoug Ambrisko } 30837b1ce13SDoug Ambrisko } 309d72a0786SJohn Baldwin error = req->ir_error; 310d72a0786SJohn Baldwin 311d72a0786SJohn Baldwin return (error); 312d72a0786SJohn Baldwin } 313d72a0786SJohn Baldwin #endif 314d72a0786SJohn Baldwin 315d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 316d72a0786SJohn Baldwin #define PTRIN(p) ((void *)(uintptr_t)(p)) 317d72a0786SJohn Baldwin #define PTROUT(p) ((uintptr_t)(p)) 31837b1ce13SDoug Ambrisko #endif 31937b1ce13SDoug Ambrisko 32037b1ce13SDoug Ambrisko static int 321d72a0786SJohn Baldwin ipmi_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, 32237b1ce13SDoug Ambrisko int flags, struct thread *td) 32337b1ce13SDoug Ambrisko { 32437b1ce13SDoug Ambrisko struct ipmi_softc *sc; 325d72a0786SJohn Baldwin struct ipmi_device *dev; 326d72a0786SJohn Baldwin struct ipmi_request *kreq; 32737b1ce13SDoug Ambrisko struct ipmi_req *req = (struct ipmi_req *)data; 32837b1ce13SDoug Ambrisko struct ipmi_recv *recv = (struct ipmi_recv *)data; 32937b1ce13SDoug Ambrisko struct ipmi_addr addr; 330d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 331d72a0786SJohn Baldwin struct ipmi_req32 *req32 = (struct ipmi_req32 *)data; 332d72a0786SJohn Baldwin struct ipmi_recv32 *recv32 = (struct ipmi_recv32 *)data; 333d72a0786SJohn Baldwin union { 334d72a0786SJohn Baldwin struct ipmi_req req; 335d72a0786SJohn Baldwin struct ipmi_recv recv; 336d72a0786SJohn Baldwin } thunk32; 337d72a0786SJohn Baldwin #endif 33837b1ce13SDoug Ambrisko int error, len; 33937b1ce13SDoug Ambrisko 340943bebd2SJohn Baldwin error = devfs_get_cdevpriv((void **)&dev); 341943bebd2SJohn Baldwin if (error) 342943bebd2SJohn Baldwin return (error); 343943bebd2SJohn Baldwin 344943bebd2SJohn Baldwin sc = cdev->si_drv1; 345d72a0786SJohn Baldwin 346d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 347d72a0786SJohn Baldwin /* Convert 32-bit structures to native. */ 348d72a0786SJohn Baldwin switch (cmd) { 349d72a0786SJohn Baldwin case IPMICTL_SEND_COMMAND_32: 350d72a0786SJohn Baldwin req = &thunk32.req; 351d72a0786SJohn Baldwin req->addr = PTRIN(req32->addr); 352d72a0786SJohn Baldwin req->addr_len = req32->addr_len; 353d72a0786SJohn Baldwin req->msgid = req32->msgid; 354d72a0786SJohn Baldwin req->msg.netfn = req32->msg.netfn; 355d72a0786SJohn Baldwin req->msg.cmd = req32->msg.cmd; 356d72a0786SJohn Baldwin req->msg.data_len = req32->msg.data_len; 357d72a0786SJohn Baldwin req->msg.data = PTRIN(req32->msg.data); 358d72a0786SJohn Baldwin break; 359d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 360d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 361d72a0786SJohn Baldwin recv = &thunk32.recv; 362d72a0786SJohn Baldwin recv->addr = PTRIN(recv32->addr); 363d72a0786SJohn Baldwin recv->addr_len = recv32->addr_len; 364d72a0786SJohn Baldwin recv->msg.data_len = recv32->msg.data_len; 365d72a0786SJohn Baldwin recv->msg.data = PTRIN(recv32->msg.data); 366d72a0786SJohn Baldwin break; 367d72a0786SJohn Baldwin } 368d72a0786SJohn Baldwin #endif 36937b1ce13SDoug Ambrisko 37037b1ce13SDoug Ambrisko switch (cmd) { 371d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 372d72a0786SJohn Baldwin case IPMICTL_SEND_COMMAND_32: 373d72a0786SJohn Baldwin #endif 37437b1ce13SDoug Ambrisko case IPMICTL_SEND_COMMAND: 375d72a0786SJohn Baldwin /* 376d72a0786SJohn Baldwin * XXX: Need to add proper handling of this. 377d72a0786SJohn Baldwin */ 37837b1ce13SDoug Ambrisko error = copyin(req->addr, &addr, sizeof(addr)); 379d72a0786SJohn Baldwin if (error) 380d72a0786SJohn Baldwin return (error); 381d72a0786SJohn Baldwin 382d72a0786SJohn Baldwin IPMI_LOCK(sc); 383d72a0786SJohn Baldwin /* clear out old stuff in queue of stuff done */ 384d72a0786SJohn Baldwin /* XXX: This seems odd. */ 385d72a0786SJohn Baldwin while ((kreq = TAILQ_FIRST(&dev->ipmi_completed_requests))) { 386d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, 387d72a0786SJohn Baldwin ir_link); 388d72a0786SJohn Baldwin dev->ipmi_requests--; 389d72a0786SJohn Baldwin ipmi_free_request(kreq); 39037b1ce13SDoug Ambrisko } 391d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 392d72a0786SJohn Baldwin 393d72a0786SJohn Baldwin kreq = ipmi_alloc_request(dev, req->msgid, 394d72a0786SJohn Baldwin IPMI_ADDR(req->msg.netfn, 0), req->msg.cmd, 395d72a0786SJohn Baldwin req->msg.data_len, IPMI_MAX_RX); 396d72a0786SJohn Baldwin error = copyin(req->msg.data, kreq->ir_request, 39737b1ce13SDoug Ambrisko req->msg.data_len); 398d72a0786SJohn Baldwin if (error) { 399d72a0786SJohn Baldwin ipmi_free_request(kreq); 400d72a0786SJohn Baldwin return (error); 40137b1ce13SDoug Ambrisko } 402d72a0786SJohn Baldwin IPMI_LOCK(sc); 403d72a0786SJohn Baldwin dev->ipmi_requests++; 404d72a0786SJohn Baldwin error = sc->ipmi_enqueue_request(sc, kreq); 405d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 406d72a0786SJohn Baldwin if (error) 407d72a0786SJohn Baldwin return (error); 408d72a0786SJohn Baldwin break; 409d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 410d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 411d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 412d72a0786SJohn Baldwin #endif 41337b1ce13SDoug Ambrisko case IPMICTL_RECEIVE_MSG_TRUNC: 41437b1ce13SDoug Ambrisko case IPMICTL_RECEIVE_MSG: 41537b1ce13SDoug Ambrisko error = copyin(recv->addr, &addr, sizeof(addr)); 416d72a0786SJohn Baldwin if (error) 417d72a0786SJohn Baldwin return (error); 418d72a0786SJohn Baldwin 419d72a0786SJohn Baldwin IPMI_LOCK(sc); 420d72a0786SJohn Baldwin kreq = TAILQ_FIRST(&dev->ipmi_completed_requests); 421d72a0786SJohn Baldwin if (kreq == NULL) { 422d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 423d72a0786SJohn Baldwin return (EAGAIN); 42437b1ce13SDoug Ambrisko } 425d72a0786SJohn Baldwin addr.channel = IPMI_BMC_CHANNEL; 426d72a0786SJohn Baldwin /* XXX */ 427d72a0786SJohn Baldwin recv->recv_type = IPMI_RESPONSE_RECV_TYPE; 428d72a0786SJohn Baldwin recv->msgid = kreq->ir_msgid; 429d72a0786SJohn Baldwin recv->msg.netfn = IPMI_REPLY_ADDR(kreq->ir_addr) >> 2; 430d72a0786SJohn Baldwin recv->msg.cmd = kreq->ir_command; 431d72a0786SJohn Baldwin error = kreq->ir_error; 432d72a0786SJohn Baldwin if (error) { 433d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, 434d72a0786SJohn Baldwin ir_link); 435d72a0786SJohn Baldwin dev->ipmi_requests--; 436d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 437d72a0786SJohn Baldwin ipmi_free_request(kreq); 438d72a0786SJohn Baldwin return (error); 439d72a0786SJohn Baldwin } 440d72a0786SJohn Baldwin len = kreq->ir_replylen + 1; 441d72a0786SJohn Baldwin if (recv->msg.data_len < len && 442d72a0786SJohn Baldwin (cmd == IPMICTL_RECEIVE_MSG 443d72a0786SJohn Baldwin #ifdef IPMICTL_RECEIVE_MSG_32 444c12dbd1dSDavid E. O'Brien || cmd == IPMICTL_RECEIVE_MSG_32 445d72a0786SJohn Baldwin #endif 446d72a0786SJohn Baldwin )) { 447d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 448d72a0786SJohn Baldwin return (EMSGSIZE); 449d72a0786SJohn Baldwin } 450d72a0786SJohn Baldwin TAILQ_REMOVE(&dev->ipmi_completed_requests, kreq, ir_link); 451d72a0786SJohn Baldwin dev->ipmi_requests--; 452d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 45337b1ce13SDoug Ambrisko len = min(recv->msg.data_len, len); 45437b1ce13SDoug Ambrisko recv->msg.data_len = len; 45537b1ce13SDoug Ambrisko error = copyout(&addr, recv->addr,sizeof(addr)); 45637b1ce13SDoug Ambrisko if (error == 0) 457d72a0786SJohn Baldwin error = copyout(&kreq->ir_compcode, recv->msg.data, 1); 458d72a0786SJohn Baldwin if (error == 0) 459d72a0786SJohn Baldwin error = copyout(kreq->ir_reply, recv->msg.data + 1, 460d72a0786SJohn Baldwin len - 1); 461d72a0786SJohn Baldwin ipmi_free_request(kreq); 462d72a0786SJohn Baldwin if (error) 463d72a0786SJohn Baldwin return (error); 464d72a0786SJohn Baldwin break; 46537b1ce13SDoug Ambrisko case IPMICTL_SET_MY_ADDRESS_CMD: 466d72a0786SJohn Baldwin IPMI_LOCK(sc); 467d72a0786SJohn Baldwin dev->ipmi_address = *(int*)data; 468d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 469d72a0786SJohn Baldwin break; 47037b1ce13SDoug Ambrisko case IPMICTL_GET_MY_ADDRESS_CMD: 471d72a0786SJohn Baldwin IPMI_LOCK(sc); 472d72a0786SJohn Baldwin *(int*)data = dev->ipmi_address; 473d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 474d72a0786SJohn Baldwin break; 47537b1ce13SDoug Ambrisko case IPMICTL_SET_MY_LUN_CMD: 476d72a0786SJohn Baldwin IPMI_LOCK(sc); 477d72a0786SJohn Baldwin dev->ipmi_lun = *(int*)data & 0x3; 478d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 479d72a0786SJohn Baldwin break; 48037b1ce13SDoug Ambrisko case IPMICTL_GET_MY_LUN_CMD: 481d72a0786SJohn Baldwin IPMI_LOCK(sc); 482d72a0786SJohn Baldwin *(int*)data = dev->ipmi_lun; 483d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 484d72a0786SJohn Baldwin break; 48537b1ce13SDoug Ambrisko case IPMICTL_SET_GETS_EVENTS_CMD: 48637b1ce13SDoug Ambrisko /* 48737b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, 48837b1ce13SDoug Ambrisko "IPMICTL_SET_GETS_EVENTS_CMD NA\n"); 48937b1ce13SDoug Ambrisko */ 490d72a0786SJohn Baldwin break; 49137b1ce13SDoug Ambrisko case IPMICTL_REGISTER_FOR_CMD: 49237b1ce13SDoug Ambrisko case IPMICTL_UNREGISTER_FOR_CMD: 493d72a0786SJohn Baldwin return (EOPNOTSUPP); 494d72a0786SJohn Baldwin default: 49537b1ce13SDoug Ambrisko device_printf(sc->ipmi_dev, "Unknown IOCTL %lX\n", cmd); 496d72a0786SJohn Baldwin return (ENOIOCTL); 49737b1ce13SDoug Ambrisko } 49837b1ce13SDoug Ambrisko 499d72a0786SJohn Baldwin #ifdef IPMICTL_SEND_COMMAND_32 500d72a0786SJohn Baldwin /* Update changed fields in 32-bit structures. */ 501d72a0786SJohn Baldwin switch (cmd) { 502d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_TRUNC_32: 503d72a0786SJohn Baldwin case IPMICTL_RECEIVE_MSG_32: 504d72a0786SJohn Baldwin recv32->recv_type = recv->recv_type; 505d72a0786SJohn Baldwin recv32->msgid = recv->msgid; 506d72a0786SJohn Baldwin recv32->msg.netfn = recv->msg.netfn; 507d72a0786SJohn Baldwin recv32->msg.cmd = recv->msg.cmd; 508d72a0786SJohn Baldwin recv32->msg.data_len = recv->msg.data_len; 50937b1ce13SDoug Ambrisko break; 51037b1ce13SDoug Ambrisko } 511d72a0786SJohn Baldwin #endif 512d72a0786SJohn Baldwin return (0); 51337b1ce13SDoug Ambrisko } 51437b1ce13SDoug Ambrisko 515d72a0786SJohn Baldwin /* 516d72a0786SJohn Baldwin * Request management. 517d72a0786SJohn Baldwin */ 51837b1ce13SDoug Ambrisko 519c869aa71SJohn Baldwin static __inline void 520c869aa71SJohn Baldwin ipmi_init_request(struct ipmi_request *req, struct ipmi_device *dev, long msgid, 521c869aa71SJohn Baldwin uint8_t addr, uint8_t command, size_t requestlen, size_t replylen) 522d72a0786SJohn Baldwin { 523d72a0786SJohn Baldwin 524d72a0786SJohn Baldwin req->ir_owner = dev; 525d72a0786SJohn Baldwin req->ir_msgid = msgid; 526d72a0786SJohn Baldwin req->ir_addr = addr; 527d72a0786SJohn Baldwin req->ir_command = command; 528d72a0786SJohn Baldwin if (requestlen) { 529d72a0786SJohn Baldwin req->ir_request = (char *)&req[1]; 530d72a0786SJohn Baldwin req->ir_requestlen = requestlen; 531d72a0786SJohn Baldwin } 532d72a0786SJohn Baldwin if (replylen) { 533d72a0786SJohn Baldwin req->ir_reply = (char *)&req[1] + requestlen; 534d72a0786SJohn Baldwin req->ir_replybuflen = replylen; 535d72a0786SJohn Baldwin } 536c869aa71SJohn Baldwin } 537c869aa71SJohn Baldwin 538c869aa71SJohn Baldwin /* Allocate a new request with request and reply buffers. */ 539c869aa71SJohn Baldwin struct ipmi_request * 540c869aa71SJohn Baldwin ipmi_alloc_request(struct ipmi_device *dev, long msgid, uint8_t addr, 541c869aa71SJohn Baldwin uint8_t command, size_t requestlen, size_t replylen) 542c869aa71SJohn Baldwin { 543c869aa71SJohn Baldwin struct ipmi_request *req; 544c869aa71SJohn Baldwin 545c869aa71SJohn Baldwin req = malloc(sizeof(struct ipmi_request) + requestlen + replylen, 546c869aa71SJohn Baldwin M_IPMI, M_WAITOK | M_ZERO); 547c869aa71SJohn Baldwin ipmi_init_request(req, dev, msgid, addr, command, requestlen, replylen); 548d72a0786SJohn Baldwin return (req); 54937b1ce13SDoug Ambrisko } 55037b1ce13SDoug Ambrisko 551d72a0786SJohn Baldwin /* Free a request no longer in use. */ 552d72a0786SJohn Baldwin void 553d72a0786SJohn Baldwin ipmi_free_request(struct ipmi_request *req) 554d72a0786SJohn Baldwin { 55537b1ce13SDoug Ambrisko 556d72a0786SJohn Baldwin free(req, M_IPMI); 55737b1ce13SDoug Ambrisko } 55837b1ce13SDoug Ambrisko 559d72a0786SJohn Baldwin /* Store a processed request on the appropriate completion queue. */ 560d72a0786SJohn Baldwin void 561d72a0786SJohn Baldwin ipmi_complete_request(struct ipmi_softc *sc, struct ipmi_request *req) 562d72a0786SJohn Baldwin { 563d72a0786SJohn Baldwin struct ipmi_device *dev; 56437b1ce13SDoug Ambrisko 565d72a0786SJohn Baldwin IPMI_LOCK_ASSERT(sc); 566d72a0786SJohn Baldwin 567d72a0786SJohn Baldwin /* 568d72a0786SJohn Baldwin * Anonymous requests (from inside the driver) always have a 569d72a0786SJohn Baldwin * waiter that we awaken. 570d72a0786SJohn Baldwin */ 571d72a0786SJohn Baldwin if (req->ir_owner == NULL) 572d72a0786SJohn Baldwin wakeup(req); 573d72a0786SJohn Baldwin else { 574d72a0786SJohn Baldwin dev = req->ir_owner; 575d72a0786SJohn Baldwin TAILQ_INSERT_TAIL(&dev->ipmi_completed_requests, req, ir_link); 576d72a0786SJohn Baldwin selwakeup(&dev->ipmi_select); 577d72a0786SJohn Baldwin if (dev->ipmi_closing) 578d72a0786SJohn Baldwin wakeup(&dev->ipmi_requests); 579d72a0786SJohn Baldwin } 58037b1ce13SDoug Ambrisko } 58137b1ce13SDoug Ambrisko 582c869aa71SJohn Baldwin /* Perform an internal driver request. */ 58337b1ce13SDoug Ambrisko int 584d72a0786SJohn Baldwin ipmi_submit_driver_request(struct ipmi_softc *sc, struct ipmi_request *req, 585d72a0786SJohn Baldwin int timo) 586d72a0786SJohn Baldwin { 58737b1ce13SDoug Ambrisko 588c869aa71SJohn Baldwin return (sc->ipmi_driver_request(sc, req, timo)); 58937b1ce13SDoug Ambrisko } 59037b1ce13SDoug Ambrisko 591d72a0786SJohn Baldwin /* 592d72a0786SJohn Baldwin * Helper routine for polled system interfaces that use 593d72a0786SJohn Baldwin * ipmi_polled_enqueue_request() to queue requests. This request 594d72a0786SJohn Baldwin * waits until there is a pending request and then returns the first 595d72a0786SJohn Baldwin * request. If the driver is shutting down, it returns NULL. 596d72a0786SJohn Baldwin */ 597d72a0786SJohn Baldwin struct ipmi_request * 598d72a0786SJohn Baldwin ipmi_dequeue_request(struct ipmi_softc *sc) 599d72a0786SJohn Baldwin { 600d72a0786SJohn Baldwin struct ipmi_request *req; 60137b1ce13SDoug Ambrisko 602d72a0786SJohn Baldwin IPMI_LOCK_ASSERT(sc); 60337b1ce13SDoug Ambrisko 604d72a0786SJohn Baldwin while (!sc->ipmi_detaching && TAILQ_EMPTY(&sc->ipmi_pending_requests)) 605c869aa71SJohn Baldwin cv_wait(&sc->ipmi_request_added, &sc->ipmi_requests_lock); 606d72a0786SJohn Baldwin if (sc->ipmi_detaching) 607d72a0786SJohn Baldwin return (NULL); 60837b1ce13SDoug Ambrisko 609d72a0786SJohn Baldwin req = TAILQ_FIRST(&sc->ipmi_pending_requests); 610d72a0786SJohn Baldwin TAILQ_REMOVE(&sc->ipmi_pending_requests, req, ir_link); 611d72a0786SJohn Baldwin return (req); 61237b1ce13SDoug Ambrisko } 61337b1ce13SDoug Ambrisko 614d72a0786SJohn Baldwin /* Default implementation of ipmi_enqueue_request() for polled interfaces. */ 615d72a0786SJohn Baldwin int 616d72a0786SJohn Baldwin ipmi_polled_enqueue_request(struct ipmi_softc *sc, struct ipmi_request *req) 617d72a0786SJohn Baldwin { 61837b1ce13SDoug Ambrisko 6195283d39bSJohn Baldwin IPMI_LOCK_ASSERT(sc); 6205283d39bSJohn Baldwin 621d72a0786SJohn Baldwin TAILQ_INSERT_TAIL(&sc->ipmi_pending_requests, req, ir_link); 622d72a0786SJohn Baldwin cv_signal(&sc->ipmi_request_added); 623d72a0786SJohn Baldwin return (0); 62437b1ce13SDoug Ambrisko } 62537b1ce13SDoug Ambrisko 62637b1ce13SDoug Ambrisko /* 62737b1ce13SDoug Ambrisko * Watchdog event handler. 62837b1ce13SDoug Ambrisko */ 62937b1ce13SDoug Ambrisko 630a46a1e76SRuslan Ermilov static int 631ea2ef993SAlexander Motin ipmi_reset_watchdog(struct ipmi_softc *sc) 632ea2ef993SAlexander Motin { 633ea2ef993SAlexander Motin struct ipmi_request *req; 634ea2ef993SAlexander Motin int error; 635ea2ef993SAlexander Motin 636ea2ef993SAlexander Motin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 637ea2ef993SAlexander Motin IPMI_RESET_WDOG, 0, 0); 638ea2ef993SAlexander Motin error = ipmi_submit_driver_request(sc, req, 0); 639ea2ef993SAlexander Motin if (error) 640ea2ef993SAlexander Motin device_printf(sc->ipmi_dev, "Failed to reset watchdog\n"); 641ea2ef993SAlexander Motin return (error); 642ea2ef993SAlexander Motin } 643ea2ef993SAlexander Motin 644ea2ef993SAlexander Motin static int 645a46a1e76SRuslan Ermilov ipmi_set_watchdog(struct ipmi_softc *sc, unsigned int sec) 646d72a0786SJohn Baldwin { 647d72a0786SJohn Baldwin struct ipmi_request *req; 648d72a0786SJohn Baldwin int error; 64937b1ce13SDoug Ambrisko 650a46a1e76SRuslan Ermilov if (sec > 0xffff / 10) 651a46a1e76SRuslan Ermilov return (EINVAL); 652a46a1e76SRuslan Ermilov 653c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 654d72a0786SJohn Baldwin IPMI_SET_WDOG, 6, 0); 65537b1ce13SDoug Ambrisko if (sec) { 656d72a0786SJohn Baldwin req->ir_request[0] = IPMI_SET_WD_TIMER_DONT_STOP 65737b1ce13SDoug Ambrisko | IPMI_SET_WD_TIMER_SMS_OS; 65814d00450SWarner Losh req->ir_request[1] = (wd_timer_actions & 0xff); 65914d00450SWarner Losh req->ir_request[2] = (wd_pretimeout_countdown & 0xff); 660d72a0786SJohn Baldwin req->ir_request[3] = 0; /* Timer use */ 661d72a0786SJohn Baldwin req->ir_request[4] = (sec * 10) & 0xff; 662a46a1e76SRuslan Ermilov req->ir_request[5] = (sec * 10) >> 8; 66337b1ce13SDoug Ambrisko } else { 664d72a0786SJohn Baldwin req->ir_request[0] = IPMI_SET_WD_TIMER_SMS_OS; 665d72a0786SJohn Baldwin req->ir_request[1] = 0; 666d72a0786SJohn Baldwin req->ir_request[2] = 0; 667d72a0786SJohn Baldwin req->ir_request[3] = 0; /* Timer use */ 668d72a0786SJohn Baldwin req->ir_request[4] = 0; 669d72a0786SJohn Baldwin req->ir_request[5] = 0; 67037b1ce13SDoug Ambrisko } 671d72a0786SJohn Baldwin error = ipmi_submit_driver_request(sc, req, 0); 672d72a0786SJohn Baldwin if (error) 673d72a0786SJohn Baldwin device_printf(sc->ipmi_dev, "Failed to set watchdog\n"); 674a46a1e76SRuslan Ermilov return (error); 67537b1ce13SDoug Ambrisko } 67637b1ce13SDoug Ambrisko 67737b1ce13SDoug Ambrisko static void 67837b1ce13SDoug Ambrisko ipmi_wd_event(void *arg, unsigned int cmd, int *error) 67937b1ce13SDoug Ambrisko { 68037b1ce13SDoug Ambrisko struct ipmi_softc *sc = arg; 68137b1ce13SDoug Ambrisko unsigned int timeout; 682a46a1e76SRuslan Ermilov int e; 68337b1ce13SDoug Ambrisko 68414d00450SWarner Losh /* Ignore requests while disabled. */ 68514d00450SWarner Losh if (!on) 686a9b3c1bfSGleb Smirnoff return; 687a9b3c1bfSGleb Smirnoff 68814d00450SWarner Losh /* 68914d00450SWarner Losh * To prevent infinite hangs, we don't let anyone pat or change 69014d00450SWarner Losh * the watchdog when we're shutting down. (See ipmi_shutdown_event().) 69114d00450SWarner Losh * However, we do want to keep patting the watchdog while we are doing 69214d00450SWarner Losh * a coredump. 69314d00450SWarner Losh */ 69414d00450SWarner Losh if (wd_in_shutdown) { 69514d00450SWarner Losh if (dumping && sc->ipmi_watchdog_active) 69614d00450SWarner Losh ipmi_reset_watchdog(sc); 69714d00450SWarner Losh return; 69814d00450SWarner Losh } 69914d00450SWarner Losh 70037b1ce13SDoug Ambrisko cmd &= WD_INTERVAL; 7019079fff5SNick Hibma if (cmd > 0 && cmd <= 63) { 702a46a1e76SRuslan Ermilov timeout = ((uint64_t)1 << cmd) / 1000000000; 703a46a1e76SRuslan Ermilov if (timeout == 0) 704a46a1e76SRuslan Ermilov timeout = 1; 70514d00450SWarner Losh if (timeout != sc->ipmi_watchdog_active || 70614d00450SWarner Losh wd_timer_actions != sc->ipmi_watchdog_actions || 70714d00450SWarner Losh wd_pretimeout_countdown != sc->ipmi_watchdog_pretimeout) { 708a46a1e76SRuslan Ermilov e = ipmi_set_watchdog(sc, timeout); 7091710852eSJohn Baldwin if (e == 0) { 710ea2ef993SAlexander Motin sc->ipmi_watchdog_active = timeout; 71114d00450SWarner Losh sc->ipmi_watchdog_actions = wd_timer_actions; 71214d00450SWarner Losh sc->ipmi_watchdog_pretimeout = wd_pretimeout_countdown; 713ea2ef993SAlexander Motin } else { 714a46a1e76SRuslan Ermilov (void)ipmi_set_watchdog(sc, 0); 715ea2ef993SAlexander Motin sc->ipmi_watchdog_active = 0; 71614d00450SWarner Losh sc->ipmi_watchdog_actions = 0; 71714d00450SWarner Losh sc->ipmi_watchdog_pretimeout = 0; 718ea2ef993SAlexander Motin } 719ea2ef993SAlexander Motin } 720ea2ef993SAlexander Motin if (sc->ipmi_watchdog_active != 0) { 721ea2ef993SAlexander Motin e = ipmi_reset_watchdog(sc); 722ea2ef993SAlexander Motin if (e == 0) { 723ea2ef993SAlexander Motin *error = 0; 724ea2ef993SAlexander Motin } else { 725ea2ef993SAlexander Motin (void)ipmi_set_watchdog(sc, 0); 726ea2ef993SAlexander Motin sc->ipmi_watchdog_active = 0; 72714d00450SWarner Losh sc->ipmi_watchdog_actions = 0; 72814d00450SWarner Losh sc->ipmi_watchdog_pretimeout = 0; 729ea2ef993SAlexander Motin } 730ea2ef993SAlexander Motin } 7311710852eSJohn Baldwin } else if (atomic_readandclear_int(&sc->ipmi_watchdog_active) != 0) { 73214d00450SWarner Losh sc->ipmi_watchdog_actions = 0; 73314d00450SWarner Losh sc->ipmi_watchdog_pretimeout = 0; 73414d00450SWarner Losh 735a46a1e76SRuslan Ermilov e = ipmi_set_watchdog(sc, 0); 736a46a1e76SRuslan Ermilov if (e != 0 && cmd == 0) 737a46a1e76SRuslan Ermilov *error = EOPNOTSUPP; 7389079fff5SNick Hibma } 73937b1ce13SDoug Ambrisko } 74037b1ce13SDoug Ambrisko 741d72a0786SJohn Baldwin static void 74214d00450SWarner Losh ipmi_shutdown_event(void *arg, unsigned int cmd, int *error) 74314d00450SWarner Losh { 74414d00450SWarner Losh struct ipmi_softc *sc = arg; 74514d00450SWarner Losh 74614d00450SWarner Losh /* Ignore event if disabled. */ 74714d00450SWarner Losh if (!on) 74814d00450SWarner Losh return; 74914d00450SWarner Losh 75014d00450SWarner Losh /* 75114d00450SWarner Losh * Positive wd_shutdown_countdown value will re-arm watchdog; 75214d00450SWarner Losh * Zero value in wd_shutdown_countdown will disable watchdog; 75314d00450SWarner Losh * Negative value in wd_shutdown_countdown will keep existing state; 75414d00450SWarner Losh * 75514d00450SWarner Losh * Revert to using a power cycle to ensure that the watchdog will 75614d00450SWarner Losh * do something useful here. Having the watchdog send an NMI 75714d00450SWarner Losh * instead is useless during shutdown, and might be ignored if an 75814d00450SWarner Losh * NMI already triggered. 75914d00450SWarner Losh */ 76014d00450SWarner Losh 76114d00450SWarner Losh wd_in_shutdown = true; 76214d00450SWarner Losh if (wd_shutdown_countdown == 0) { 76314d00450SWarner Losh /* disable watchdog */ 76414d00450SWarner Losh ipmi_set_watchdog(sc, 0); 76514d00450SWarner Losh sc->ipmi_watchdog_active = 0; 76614d00450SWarner Losh } else if (wd_shutdown_countdown > 0) { 76714d00450SWarner Losh /* set desired action and time, and, reset watchdog */ 76814d00450SWarner Losh wd_timer_actions = IPMI_SET_WD_ACTION_POWER_CYCLE; 76914d00450SWarner Losh ipmi_set_watchdog(sc, wd_shutdown_countdown); 77014d00450SWarner Losh sc->ipmi_watchdog_active = wd_shutdown_countdown; 77114d00450SWarner Losh ipmi_reset_watchdog(sc); 77214d00450SWarner Losh } 77314d00450SWarner Losh } 77414d00450SWarner Losh 77514d00450SWarner Losh static void 7761170c2feSWarner Losh ipmi_power_cycle(void *arg, int howto) 7771170c2feSWarner Losh { 7781170c2feSWarner Losh struct ipmi_softc *sc = arg; 7791170c2feSWarner Losh struct ipmi_request *req; 7801170c2feSWarner Losh 7811170c2feSWarner Losh /* 7821170c2feSWarner Losh * Ignore everything except power cycling requests 7831170c2feSWarner Losh */ 7841170c2feSWarner Losh if ((howto & RB_POWERCYCLE) == 0) 7851170c2feSWarner Losh return; 7861170c2feSWarner Losh 7871170c2feSWarner Losh device_printf(sc->ipmi_dev, "Power cycling using IPMI\n"); 7881170c2feSWarner Losh 7891170c2feSWarner Losh /* 7901170c2feSWarner Losh * Send a CHASSIS_CONTROL command to the CHASSIS device, subcommand 2 7911170c2feSWarner Losh * as described in IPMI v2.0 spec section 28.3. 7921170c2feSWarner Losh */ 7931170c2feSWarner Losh IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_CHASSIS_REQUEST, 0), 7941170c2feSWarner Losh IPMI_CHASSIS_CONTROL, 1, 0); 7951170c2feSWarner Losh req->ir_request[0] = IPMI_CC_POWER_CYCLE; 7961170c2feSWarner Losh 7971170c2feSWarner Losh ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); 7981170c2feSWarner Losh 7991170c2feSWarner Losh if (req->ir_error != 0 || req->ir_compcode != 0) { 8001170c2feSWarner Losh device_printf(sc->ipmi_dev, "Power cycling via IPMI failed code %#x %#x\n", 8011170c2feSWarner Losh req->ir_error, req->ir_compcode); 8021170c2feSWarner Losh return; 8031170c2feSWarner Losh } 8041170c2feSWarner Losh 8051170c2feSWarner Losh /* 80616f0063eSWarner Losh * BMCs are notoriously slow, give it cyle_wait seconds for the power 8071170c2feSWarner Losh * down leg of the power cycle. If that fails, fallback to the next 8081170c2feSWarner Losh * hanlder in the shutdown_final chain and/or the platform failsafe. 8091170c2feSWarner Losh */ 81016f0063eSWarner Losh DELAY(cycle_wait * 1000 * 1000); 8111170c2feSWarner Losh device_printf(sc->ipmi_dev, "Power cycling via IPMI timed out\n"); 8121170c2feSWarner Losh } 8131170c2feSWarner Losh 8141170c2feSWarner Losh static void 815d72a0786SJohn Baldwin ipmi_startup(void *arg) 816d72a0786SJohn Baldwin { 817d72a0786SJohn Baldwin struct ipmi_softc *sc = arg; 818d72a0786SJohn Baldwin struct ipmi_request *req; 819d72a0786SJohn Baldwin device_t dev; 820d72a0786SJohn Baldwin int error, i; 821d72a0786SJohn Baldwin 822d72a0786SJohn Baldwin config_intrhook_disestablish(&sc->ipmi_ich); 823d72a0786SJohn Baldwin dev = sc->ipmi_dev; 824d72a0786SJohn Baldwin 825d72a0786SJohn Baldwin /* Initialize interface-independent state. */ 826c869aa71SJohn Baldwin mtx_init(&sc->ipmi_requests_lock, "ipmi requests", NULL, MTX_DEF); 827c869aa71SJohn Baldwin mtx_init(&sc->ipmi_io_lock, "ipmi io", NULL, MTX_DEF); 828d72a0786SJohn Baldwin cv_init(&sc->ipmi_request_added, "ipmireq"); 829d72a0786SJohn Baldwin TAILQ_INIT(&sc->ipmi_pending_requests); 830d72a0786SJohn Baldwin 831d72a0786SJohn Baldwin /* Initialize interface-dependent state. */ 832d72a0786SJohn Baldwin error = sc->ipmi_startup(sc); 833d72a0786SJohn Baldwin if (error) { 834d72a0786SJohn Baldwin device_printf(dev, "Failed to initialize interface: %d\n", 835d72a0786SJohn Baldwin error); 836d72a0786SJohn Baldwin return; 837d72a0786SJohn Baldwin } 838d72a0786SJohn Baldwin 839d72a0786SJohn Baldwin /* Send a GET_DEVICE_ID request. */ 840c869aa71SJohn Baldwin IPMI_ALLOC_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 841d72a0786SJohn Baldwin IPMI_GET_DEVICE_ID, 0, 15); 842d72a0786SJohn Baldwin 843d72a0786SJohn Baldwin error = ipmi_submit_driver_request(sc, req, MAX_TIMEOUT); 844d72a0786SJohn Baldwin if (error == EWOULDBLOCK) { 845d72a0786SJohn Baldwin device_printf(dev, "Timed out waiting for GET_DEVICE_ID\n"); 846d72a0786SJohn Baldwin return; 847d72a0786SJohn Baldwin } else if (error) { 848d72a0786SJohn Baldwin device_printf(dev, "Failed GET_DEVICE_ID: %d\n", error); 849d72a0786SJohn Baldwin return; 850d72a0786SJohn Baldwin } else if (req->ir_compcode != 0) { 851d72a0786SJohn Baldwin device_printf(dev, 852d72a0786SJohn Baldwin "Bad completion code for GET_DEVICE_ID: %d\n", 853d72a0786SJohn Baldwin req->ir_compcode); 854d72a0786SJohn Baldwin return; 855d72a0786SJohn Baldwin } else if (req->ir_replylen < 5) { 856d72a0786SJohn Baldwin device_printf(dev, "Short reply for GET_DEVICE_ID: %d\n", 857d72a0786SJohn Baldwin req->ir_replylen); 858d72a0786SJohn Baldwin return; 859d72a0786SJohn Baldwin } 860d72a0786SJohn Baldwin 86114689886SRuslan Ermilov device_printf(dev, "IPMI device rev. %d, firmware rev. %d.%d%d, " 8621170c2feSWarner Losh "version %d.%d, device support mask %#x\n", 863d72a0786SJohn Baldwin req->ir_reply[1] & 0x0f, 86414689886SRuslan Ermilov req->ir_reply[2] & 0x7f, req->ir_reply[3] >> 4, req->ir_reply[3] & 0x0f, 8651170c2feSWarner Losh req->ir_reply[4] & 0x0f, req->ir_reply[4] >> 4, req->ir_reply[5]); 8661170c2feSWarner Losh 8671170c2feSWarner Losh sc->ipmi_dev_support = req->ir_reply[5]; 868d72a0786SJohn Baldwin 869c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 870d72a0786SJohn Baldwin IPMI_CLEAR_FLAGS, 1, 0); 871d72a0786SJohn Baldwin 872d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req, 0); 873d72a0786SJohn Baldwin 874d72a0786SJohn Baldwin /* XXX: Magic numbers */ 875d72a0786SJohn Baldwin if (req->ir_compcode == 0xc0) { 876d72a0786SJohn Baldwin device_printf(dev, "Clear flags is busy\n"); 877d72a0786SJohn Baldwin } 878d72a0786SJohn Baldwin if (req->ir_compcode == 0xc1) { 879d72a0786SJohn Baldwin device_printf(dev, "Clear flags illegal\n"); 880d72a0786SJohn Baldwin } 881d72a0786SJohn Baldwin 882d72a0786SJohn Baldwin for (i = 0; i < 8; i++) { 883c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 884d72a0786SJohn Baldwin IPMI_GET_CHANNEL_INFO, 1, 0); 885d72a0786SJohn Baldwin req->ir_request[0] = i; 886d72a0786SJohn Baldwin 887d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req, 0); 888d72a0786SJohn Baldwin 889c869aa71SJohn Baldwin if (req->ir_compcode != 0) 890d72a0786SJohn Baldwin break; 891d72a0786SJohn Baldwin } 892d72a0786SJohn Baldwin device_printf(dev, "Number of channels %d\n", i); 893d72a0786SJohn Baldwin 8949662eef5SJohn Baldwin /* 8959662eef5SJohn Baldwin * Probe for watchdog, but only for backends which support 8969662eef5SJohn Baldwin * polled driver requests. 8979662eef5SJohn Baldwin */ 8989662eef5SJohn Baldwin if (sc->ipmi_driver_requests_polled) { 899c869aa71SJohn Baldwin IPMI_INIT_DRIVER_REQUEST(req, IPMI_ADDR(IPMI_APP_REQUEST, 0), 900d72a0786SJohn Baldwin IPMI_GET_WDOG, 0, 0); 901d72a0786SJohn Baldwin 902d72a0786SJohn Baldwin ipmi_submit_driver_request(sc, req, 0); 903d72a0786SJohn Baldwin 904d72a0786SJohn Baldwin if (req->ir_compcode == 0x00) { 905d72a0786SJohn Baldwin device_printf(dev, "Attached watchdog\n"); 906d72a0786SJohn Baldwin /* register the watchdog event handler */ 9079662eef5SJohn Baldwin sc->ipmi_watchdog_tag = EVENTHANDLER_REGISTER( 9089662eef5SJohn Baldwin watchdog_list, ipmi_wd_event, sc, 0); 90914d00450SWarner Losh sc->ipmi_shutdown_tag = EVENTHANDLER_REGISTER( 91014d00450SWarner Losh shutdown_pre_sync, ipmi_shutdown_event, 91114d00450SWarner Losh sc, 0); 9129662eef5SJohn Baldwin } 913d72a0786SJohn Baldwin } 914d72a0786SJohn Baldwin 915943bebd2SJohn Baldwin sc->ipmi_cdev = make_dev(&ipmi_cdevsw, device_get_unit(dev), 916d72a0786SJohn Baldwin UID_ROOT, GID_OPERATOR, 0660, "ipmi%d", device_get_unit(dev)); 917943bebd2SJohn Baldwin if (sc->ipmi_cdev == NULL) { 918d72a0786SJohn Baldwin device_printf(dev, "Failed to create cdev\n"); 919d72a0786SJohn Baldwin return; 920d72a0786SJohn Baldwin } 921943bebd2SJohn Baldwin sc->ipmi_cdev->si_drv1 = sc; 9221170c2feSWarner Losh 9231170c2feSWarner Losh /* 92414d00450SWarner Losh * Set initial watchdog state. If desired, set an initial 92514d00450SWarner Losh * watchdog on startup. Or, if the watchdog device is 92614d00450SWarner Losh * disabled, clear any existing watchdog. 92714d00450SWarner Losh */ 92814d00450SWarner Losh if (on && wd_startup_countdown > 0) { 92914d00450SWarner Losh wd_timer_actions = IPMI_SET_WD_ACTION_POWER_CYCLE; 93014d00450SWarner Losh if (ipmi_set_watchdog(sc, wd_startup_countdown) == 0 && 93114d00450SWarner Losh ipmi_reset_watchdog(sc) == 0) { 93214d00450SWarner Losh sc->ipmi_watchdog_active = wd_startup_countdown; 93314d00450SWarner Losh sc->ipmi_watchdog_actions = wd_timer_actions; 93414d00450SWarner Losh sc->ipmi_watchdog_pretimeout = wd_pretimeout_countdown; 93514d00450SWarner Losh } else 93614d00450SWarner Losh (void)ipmi_set_watchdog(sc, 0); 93714d00450SWarner Losh ipmi_reset_watchdog(sc); 93814d00450SWarner Losh } else if (!on) 93914d00450SWarner Losh (void)ipmi_set_watchdog(sc, 0); 94014d00450SWarner Losh /* 941*26649bb5SConrad Meyer * Power cycle the system off using IPMI. We use last - 2 since we don't 9421170c2feSWarner Losh * handle all the other kinds of reboots. We'll let others handle them. 9431170c2feSWarner Losh * We only try to do this if the BMC supports the Chassis device. 9441170c2feSWarner Losh */ 9451170c2feSWarner Losh if (sc->ipmi_dev_support & IPMI_ADS_CHASSIS) { 9461170c2feSWarner Losh device_printf(dev, "Establishing power cycle handler\n"); 9471170c2feSWarner Losh sc->ipmi_power_cycle_tag = EVENTHANDLER_REGISTER(shutdown_final, 948*26649bb5SConrad Meyer ipmi_power_cycle, sc, SHUTDOWN_PRI_LAST - 2); 9491170c2feSWarner Losh } 950d72a0786SJohn Baldwin } 951d72a0786SJohn Baldwin 95237b1ce13SDoug Ambrisko int 95337b1ce13SDoug Ambrisko ipmi_attach(device_t dev) 95437b1ce13SDoug Ambrisko { 95537b1ce13SDoug Ambrisko struct ipmi_softc *sc = device_get_softc(dev); 956d72a0786SJohn Baldwin int error; 95737b1ce13SDoug Ambrisko 958d72a0786SJohn Baldwin if (sc->ipmi_irq_res != NULL && sc->ipmi_intr != NULL) { 959d72a0786SJohn Baldwin error = bus_setup_intr(dev, sc->ipmi_irq_res, INTR_TYPE_MISC, 960ef544f63SPaolo Pisati NULL, sc->ipmi_intr, sc, &sc->ipmi_irq); 961d72a0786SJohn Baldwin if (error) { 962d72a0786SJohn Baldwin device_printf(dev, "can't set up interrupt\n"); 963d72a0786SJohn Baldwin return (error); 96437b1ce13SDoug Ambrisko } 96537b1ce13SDoug Ambrisko } 96637b1ce13SDoug Ambrisko 967d72a0786SJohn Baldwin bzero(&sc->ipmi_ich, sizeof(struct intr_config_hook)); 968d72a0786SJohn Baldwin sc->ipmi_ich.ich_func = ipmi_startup; 969d72a0786SJohn Baldwin sc->ipmi_ich.ich_arg = sc; 970d72a0786SJohn Baldwin if (config_intrhook_establish(&sc->ipmi_ich) != 0) { 971d72a0786SJohn Baldwin device_printf(dev, "can't establish configuration hook\n"); 972d72a0786SJohn Baldwin return (ENOMEM); 97337b1ce13SDoug Ambrisko } 97437b1ce13SDoug Ambrisko 97537b1ce13SDoug Ambrisko ipmi_attached = 1; 976d72a0786SJohn Baldwin return (0); 97737b1ce13SDoug Ambrisko } 97837b1ce13SDoug Ambrisko 97937b1ce13SDoug Ambrisko int 98037b1ce13SDoug Ambrisko ipmi_detach(device_t dev) 98137b1ce13SDoug Ambrisko { 98237b1ce13SDoug Ambrisko struct ipmi_softc *sc; 98337b1ce13SDoug Ambrisko 98437b1ce13SDoug Ambrisko sc = device_get_softc(dev); 985d72a0786SJohn Baldwin 986d72a0786SJohn Baldwin /* Fail if there are any open handles. */ 987d72a0786SJohn Baldwin IPMI_LOCK(sc); 988943bebd2SJohn Baldwin if (sc->ipmi_opened) { 989d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 990d72a0786SJohn Baldwin return (EBUSY); 991d72a0786SJohn Baldwin } 992d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 993943bebd2SJohn Baldwin if (sc->ipmi_cdev) 994943bebd2SJohn Baldwin destroy_dev(sc->ipmi_cdev); 995d72a0786SJohn Baldwin 996d72a0786SJohn Baldwin /* Detach from watchdog handling and turn off watchdog. */ 99714d00450SWarner Losh if (sc->ipmi_shutdown_tag) 99814d00450SWarner Losh EVENTHANDLER_DEREGISTER(shutdown_pre_sync, 99914d00450SWarner Losh sc->ipmi_shutdown_tag); 1000d72a0786SJohn Baldwin if (sc->ipmi_watchdog_tag) { 1001d72a0786SJohn Baldwin EVENTHANDLER_DEREGISTER(watchdog_list, sc->ipmi_watchdog_tag); 1002d72a0786SJohn Baldwin ipmi_set_watchdog(sc, 0); 1003d72a0786SJohn Baldwin } 1004d72a0786SJohn Baldwin 10051170c2feSWarner Losh /* Detach from shutdown handling for power cycle reboot */ 10061170c2feSWarner Losh if (sc->ipmi_power_cycle_tag) 10071170c2feSWarner Losh EVENTHANDLER_DEREGISTER(shutdown_final, sc->ipmi_power_cycle_tag); 10081170c2feSWarner Losh 1009d72a0786SJohn Baldwin /* XXX: should use shutdown callout I think. */ 1010d72a0786SJohn Baldwin /* If the backend uses a kthread, shut it down. */ 1011d72a0786SJohn Baldwin IPMI_LOCK(sc); 1012d72a0786SJohn Baldwin sc->ipmi_detaching = 1; 1013d72a0786SJohn Baldwin if (sc->ipmi_kthread) { 1014d72a0786SJohn Baldwin cv_broadcast(&sc->ipmi_request_added); 1015c869aa71SJohn Baldwin msleep(sc->ipmi_kthread, &sc->ipmi_requests_lock, 0, 1016c869aa71SJohn Baldwin "ipmi_wait", 0); 1017d72a0786SJohn Baldwin } 1018d72a0786SJohn Baldwin IPMI_UNLOCK(sc); 1019d72a0786SJohn Baldwin if (sc->ipmi_irq) 1020d72a0786SJohn Baldwin bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); 1021d72a0786SJohn Baldwin 1022d72a0786SJohn Baldwin ipmi_release_resources(dev); 1023c869aa71SJohn Baldwin mtx_destroy(&sc->ipmi_io_lock); 1024c869aa71SJohn Baldwin mtx_destroy(&sc->ipmi_requests_lock); 1025d72a0786SJohn Baldwin return (0); 1026d72a0786SJohn Baldwin } 1027d72a0786SJohn Baldwin 1028d72a0786SJohn Baldwin void 1029d72a0786SJohn Baldwin ipmi_release_resources(device_t dev) 1030d72a0786SJohn Baldwin { 1031d72a0786SJohn Baldwin struct ipmi_softc *sc; 1032d72a0786SJohn Baldwin int i; 1033d72a0786SJohn Baldwin 1034d72a0786SJohn Baldwin sc = device_get_softc(dev); 1035d72a0786SJohn Baldwin if (sc->ipmi_irq) 1036d72a0786SJohn Baldwin bus_teardown_intr(dev, sc->ipmi_irq_res, sc->ipmi_irq); 1037d72a0786SJohn Baldwin if (sc->ipmi_irq_res) 1038d72a0786SJohn Baldwin bus_release_resource(dev, SYS_RES_IRQ, sc->ipmi_irq_rid, 1039d72a0786SJohn Baldwin sc->ipmi_irq_res); 1040d72a0786SJohn Baldwin for (i = 0; i < MAX_RES; i++) 1041d72a0786SJohn Baldwin if (sc->ipmi_io_res[i]) 1042d72a0786SJohn Baldwin bus_release_resource(dev, sc->ipmi_io_type, 1043d72a0786SJohn Baldwin sc->ipmi_io_rid + i, sc->ipmi_io_res[i]); 1044d72a0786SJohn Baldwin } 1045d72a0786SJohn Baldwin 1046d72a0786SJohn Baldwin devclass_t ipmi_devclass; 1047d72a0786SJohn Baldwin 1048d72a0786SJohn Baldwin /* XXX: Why? */ 1049d72a0786SJohn Baldwin static void 1050d72a0786SJohn Baldwin ipmi_unload(void *arg) 1051d72a0786SJohn Baldwin { 1052d72a0786SJohn Baldwin device_t * devs; 1053d72a0786SJohn Baldwin int count; 1054d72a0786SJohn Baldwin int i; 1055d72a0786SJohn Baldwin 10563991dbf3SDoug Ambrisko if (ipmi_devclass == NULL) 10573991dbf3SDoug Ambrisko return; 1058bec0c98eSJohn Baldwin if (devclass_get_devices(ipmi_devclass, &devs, &count) != 0) 1059bec0c98eSJohn Baldwin return; 1060d72a0786SJohn Baldwin for (i = 0; i < count; i++) 1061d72a0786SJohn Baldwin device_delete_child(device_get_parent(devs[i]), devs[i]); 1062bec0c98eSJohn Baldwin free(devs, M_TEMP); 1063d72a0786SJohn Baldwin } 1064d72a0786SJohn Baldwin SYSUNINIT(ipmi_unload, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipmi_unload, NULL); 1065d72a0786SJohn Baldwin 106619d61f3fSDoug Ambrisko #ifdef IMPI_DEBUG 106737b1ce13SDoug Ambrisko static void 1068d72a0786SJohn Baldwin dump_buf(u_char *data, int len) 1069d72a0786SJohn Baldwin { 107037b1ce13SDoug Ambrisko char buf[20]; 107137b1ce13SDoug Ambrisko char line[1024]; 107237b1ce13SDoug Ambrisko char temp[30]; 107337b1ce13SDoug Ambrisko int count = 0; 107437b1ce13SDoug Ambrisko int i=0; 107537b1ce13SDoug Ambrisko 107637b1ce13SDoug Ambrisko printf("Address %p len %d\n", data, len); 107737b1ce13SDoug Ambrisko if (len > 256) 107837b1ce13SDoug Ambrisko len = 256; 107937b1ce13SDoug Ambrisko line[0] = '\000'; 108037b1ce13SDoug Ambrisko for (; len > 0; len--, data++) { 108137b1ce13SDoug Ambrisko sprintf(temp, "%02x ", *data); 108237b1ce13SDoug Ambrisko strcat(line, temp); 108337b1ce13SDoug Ambrisko if (*data >= ' ' && *data <= '~') 108437b1ce13SDoug Ambrisko buf[count] = *data; 108537b1ce13SDoug Ambrisko else if (*data >= 'A' && *data <= 'Z') 108637b1ce13SDoug Ambrisko buf[count] = *data; 108737b1ce13SDoug Ambrisko else 108837b1ce13SDoug Ambrisko buf[count] = '.'; 108937b1ce13SDoug Ambrisko if (++count == 16) { 109037b1ce13SDoug Ambrisko buf[count] = '\000'; 109137b1ce13SDoug Ambrisko count = 0; 109237b1ce13SDoug Ambrisko printf(" %3x %s %s\n", i, line, buf); 109337b1ce13SDoug Ambrisko i+=16; 109437b1ce13SDoug Ambrisko line[0] = '\000'; 109537b1ce13SDoug Ambrisko } 109637b1ce13SDoug Ambrisko } 109737b1ce13SDoug Ambrisko buf[count] = '\000'; 109837b1ce13SDoug Ambrisko 109937b1ce13SDoug Ambrisko for (; count != 16; count++) { 110037b1ce13SDoug Ambrisko strcat(line, " "); 110137b1ce13SDoug Ambrisko } 110237b1ce13SDoug Ambrisko printf(" %3x %s %s\n", i, line, buf); 110337b1ce13SDoug Ambrisko } 110437b1ce13SDoug Ambrisko #endif 1105