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 } 285