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