1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Delphix (c) 2012 by Delphix. All rights reserved.
  24  */
  25 
  26 
  27 /*
  28  * Dump driver.  Provides ioctls to get/set crash dump configuration.
  29  */
  30 
  31 #include <sys/types.h>
  32 #include <sys/param.h>
  33 #include <sys/systm.h>
  34 #include <sys/vnode.h>
  35 #include <sys/uio.h>
  36 #include <sys/cred.h>
  37 #include <sys/kmem.h>
  38 #include <sys/errno.h>
  39 #include <sys/modctl.h>
  40 #include <sys/dumphdr.h>
  41 #include <sys/dumpadm.h>
  42 #include <sys/pathname.h>
  43 #include <sys/file.h>
  44 #include <vm/anon.h>
  45 #include <sys/stat.h>
  46 #include <sys/conf.h>
  47 #include <sys/ddi.h>
  48 #include <sys/sunddi.h>
  49 
  50 static dev_info_t *dump_devi;
  51 
  52 static int
  53 dump_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
  54 {
  55         if (cmd != DDI_ATTACH)
  56                 return (DDI_FAILURE);
  57         if (ddi_create_minor_node(devi, "dump", S_IFCHR, 0, DDI_PSEUDO, NULL) ==
  58             DDI_FAILURE) {
  59                 ddi_remove_minor_node(devi, NULL);
  60                 return (DDI_FAILURE);
  61         }
  62         dump_devi = devi;
  63         return (DDI_SUCCESS);
  64 }
  65 
  66 static int
  67 dump_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
  68 {
  69         if (cmd != DDI_DETACH)
  70                 return (DDI_FAILURE);
  71         ddi_remove_minor_node(devi, NULL);
  72         return (DDI_SUCCESS);
  73 }
  74 
  75 /*ARGSUSED*/
  76 static int
  77 dump_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
  78 {
  79         switch (infocmd) {
  80         case DDI_INFO_DEVT2DEVINFO:
  81                 *result = dump_devi;
  82                 return (DDI_SUCCESS);
  83         case DDI_INFO_DEVT2INSTANCE:
  84                 *result = 0;
  85                 return (DDI_SUCCESS);
  86         }
  87         return (DDI_FAILURE);
  88 }
  89 
  90 /*ARGSUSED*/
  91 int
  92 dump_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp)
  93 {
  94         uint64_t size;
  95         uint64_t dumpsize_in_pages;
  96         int error = 0;
  97         char *pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
  98         char uuidbuf[36 + 1];
  99         size_t len;
 100         vnode_t *vp;
 101 
 102         switch (cmd) {
 103         case DIOCGETDUMPSIZE:
 104                 if (dump_conflags & DUMP_ALL)
 105                         size = ptob((uint64_t)physmem) / DUMP_COMPRESS_RATIO;
 106                 else {
 107                         /*
 108                          * We can't give a good answer for the DUMP_CURPROC
 109                          * because we won't know which process to use until it
 110                          * causes a panic.  We'll therefore punt and give the
 111                          * caller the size for the kernel.
 112                          *
 113                          * This kernel size equation takes care of the
 114                          * boot time kernel footprint and also accounts
 115                          * for availrmem changes due to user explicit locking.
 116                          * Refer to common/vm/vm_page.c for an explanation
 117                          * of these counters.
 118                          */
 119                         dumpsize_in_pages = (physinstalled - obp_pages -
 120                             availrmem -
 121                             anon_segkp_pages_locked -
 122                             k_anoninfo.ani_mem_resv -
 123                             pages_locked -
 124                             pages_claimed -
 125                             pages_useclaim);
 126 
 127                         /*
 128                          * Protect against vm vagaries.
 129                          */
 130                         if (dumpsize_in_pages > (uint64_t)physmem)
 131                                 dumpsize_in_pages = (uint64_t)physmem;
 132 
 133                         size = ptob(dumpsize_in_pages) / DUMP_COMPRESS_RATIO;
 134                 }
 135                 if (copyout(&size, (void *)arg, sizeof (size)) < 0)
 136                         error = EFAULT;
 137                 break;
 138 
 139         case DIOCGETCONF:
 140                 mutex_enter(&dump_lock);
 141                 *rvalp = dump_conflags;
 142                 if (dumpvp && !(dumpvp->v_flag & VISSWAP))
 143                         *rvalp |= DUMP_EXCL;
 144                 mutex_exit(&dump_lock);
 145                 break;
 146 
 147         case DIOCSETCONF:
 148                 mutex_enter(&dump_lock);
 149                 if (arg == DUMP_KERNEL || arg == DUMP_ALL ||
 150                     arg == DUMP_CURPROC)
 151                         dump_conflags = arg;
 152                 else
 153                         error = EINVAL;
 154                 mutex_exit(&dump_lock);
 155                 break;
 156 
 157         case DIOCGETDEV:
 158                 mutex_enter(&dump_lock);
 159                 if (dumppath == NULL) {
 160                         mutex_exit(&dump_lock);
 161                         error = ENODEV;
 162                         break;
 163                 }
 164                 (void) strcpy(pathbuf, dumppath);
 165                 mutex_exit(&dump_lock);
 166                 error = copyoutstr(pathbuf, (void *)arg, MAXPATHLEN, NULL);
 167                 break;
 168 
 169         case DIOCSETDEV:
 170         case DIOCTRYDEV:
 171                 if ((error = copyinstr((char *)arg, pathbuf, MAXPATHLEN,
 172                     NULL)) != 0 || (error = lookupname(pathbuf, UIO_SYSSPACE,
 173                     FOLLOW, NULLVPP, &vp)) != 0)
 174                         break;
 175                 mutex_enter(&dump_lock);
 176                 if (vp->v_type == VBLK)
 177                         error = dumpinit(vp, pathbuf, cmd == DIOCTRYDEV);
 178                 else
 179                         error = ENOTBLK;
 180                 mutex_exit(&dump_lock);
 181                 VN_RELE(vp);
 182                 break;
 183 
 184         case DIOCDUMP:
 185                 mutex_enter(&dump_lock);
 186                 if (dumpvp == NULL)
 187                         error = ENODEV;
 188                 else if (dumpvp->v_flag & VISSWAP)
 189                         error = EBUSY;
 190                 else
 191                         dumpsys();
 192                 mutex_exit(&dump_lock);
 193                 break;
 194 
 195         case DIOCSETUUID:
 196                 if ((error = copyinstr((char *)arg, uuidbuf, sizeof (uuidbuf),
 197                     &len)) != 0)
 198                         break;
 199 
 200                 if (len != 37) {
 201                         error = EINVAL;
 202                         break;
 203                 }
 204 
 205                 error = dump_set_uuid(uuidbuf);
 206                 break;
 207 
 208         case DIOCGETUUID:
 209                 error = copyoutstr(dump_get_uuid(), (void *)arg, 37, NULL);
 210                 break;
 211 
 212         case DIOCRMDEV:
 213                 mutex_enter(&dump_lock);
 214                 if (dumpvp != NULL)
 215                         dumpfini();
 216                 mutex_exit(&dump_lock);
 217                 break;
 218 
 219         default:
 220                 error = ENXIO;
 221         }
 222 
 223         kmem_free(pathbuf, MAXPATHLEN);
 224         return (error);
 225 }
 226 
 227 struct cb_ops dump_cb_ops = {
 228         nulldev,                /* open */
 229         nulldev,                /* close */
 230         nodev,                  /* strategy */
 231         nodev,                  /* print */
 232         nodev,                  /* dump */
 233         nodev,                  /* read */
 234         nodev,                  /* write */
 235         dump_ioctl,             /* ioctl */
 236         nodev,                  /* devmap */
 237         nodev,                  /* mmap */
 238         nodev,                  /* segmap */
 239         nochpoll,               /* poll */
 240         ddi_prop_op,            /* prop_op */
 241         0,                      /* streamtab  */
 242         D_NEW|D_MP              /* Driver compatibility flag */
 243 };
 244 
 245 struct dev_ops dump_ops = {
 246         DEVO_REV,               /* devo_rev, */
 247         0,                      /* refcnt */
 248         dump_info,              /* info */
 249         nulldev,                /* identify */
 250         nulldev,                /* probe */
 251         dump_attach,            /* attach */
 252         dump_detach,            /* detach */
 253         nodev,                  /* reset */
 254         &dump_cb_ops,               /* driver operations */
 255         (struct bus_ops *)0,    /* bus operations */
 256         NULL,                   /* power */
 257         ddi_quiesce_not_needed,         /* quiesce */
 258 };
 259 
 260 static struct modldrv modldrv = {
 261         &mod_driverops, "crash dump driver", &dump_ops,
 262 };
 263 
 264 static struct modlinkage modlinkage = {
 265         MODREV_1, (void *)&modldrv, NULL
 266 };
 267 
 268 int
 269 _init(void)
 270 {
 271         return (mod_install(&modlinkage));
 272 }
 273 
 274 int
 275 _fini(void)
 276 {
 277         return (mod_remove(&modlinkage));
 278 }
 279 
 280 int
 281 _info(struct modinfo *modinfop)
 282 {
 283         return (mod_info(&modlinkage, modinfop));
 284 }