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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 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 51 static dev_info_t *dump_devi; 52 53 static int 54 dump_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 55 { 56 if (cmd != DDI_ATTACH) 57 return (DDI_FAILURE); 58 if (ddi_create_minor_node(devi, "dump", S_IFCHR, 0, DDI_PSEUDO, NULL) == 59 DDI_FAILURE) { 60 ddi_remove_minor_node(devi, NULL); 61 return (DDI_FAILURE); 62 } 63 dump_devi = devi; 64 return (DDI_SUCCESS); 65 } 66 67 static int 68 dump_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 69 { 70 if (cmd != DDI_DETACH) 71 return (DDI_FAILURE); 72 ddi_remove_minor_node(devi, NULL); 73 return (DDI_SUCCESS); 74 } 75 76 /*ARGSUSED*/ 77 static int 78 dump_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 79 { 80 switch (infocmd) { 81 case DDI_INFO_DEVT2DEVINFO: 82 *result = dump_devi; 83 return (DDI_SUCCESS); 84 case DDI_INFO_DEVT2INSTANCE: 85 *result = 0; 86 return (DDI_SUCCESS); 87 } 88 return (DDI_FAILURE); 89 } 90 91 /*ARGSUSED*/ 92 int 93 dump_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp) 94 { 95 uint64_t size; 96 uint64_t dumpsize_in_pages; 97 int error = 0; 98 char *pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 99 vnode_t *vp; 100 101 switch (cmd) { 102 case DIOCGETDUMPSIZE: 103 if (dump_conflags & DUMP_ALL) 104 size = ptob((uint64_t)physmem) / DUMP_COMPRESS_RATIO; 105 else { 106 /* 107 * We can't give a good answer for the DUMP_CURPROC 108 * because we won't know which process to use until it 109 * causes a panic. We'll therefore punt and give the 110 * caller the size for the kernel. 111 * 112 * This kernel size equation takes care of the 113 * boot time kernel footprint and also accounts 114 * for availrmem changes due to user explicit locking. 115 * Refer to common/vm/vm_page.c for an explanation 116 * of these counters. 117 */ 118 dumpsize_in_pages = (physinstalled - obp_pages - 119 availrmem - 120 anon_segkp_pages_locked - 121 k_anoninfo.ani_mem_resv - 122 segvn_pages_locked - 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 default: 196 error = ENXIO; 197 } 198 199 kmem_free(pathbuf, MAXPATHLEN); 200 return (error); 201 } 202 203 struct cb_ops dump_cb_ops = { 204 nulldev, /* open */ 205 nulldev, /* close */ 206 nodev, /* strategy */ 207 nodev, /* print */ 208 nodev, /* dump */ 209 nodev, /* read */ 210 nodev, /* write */ 211 dump_ioctl, /* ioctl */ 212 nodev, /* devmap */ 213 nodev, /* mmap */ 214 nodev, /* segmap */ 215 nochpoll, /* poll */ 216 ddi_prop_op, /* prop_op */ 217 0, /* streamtab */ 218 D_NEW|D_MP /* Driver compatibility flag */ 219 }; 220 221 struct dev_ops dump_ops = { 222 DEVO_REV, /* devo_rev, */ 223 0, /* refcnt */ 224 dump_info, /* info */ 225 nulldev, /* identify */ 226 nulldev, /* probe */ 227 dump_attach, /* attach */ 228 dump_detach, /* detach */ 229 nodev, /* reset */ 230 &dump_cb_ops, /* driver operations */ 231 (struct bus_ops *)0 /* bus operations */ 232 }; 233 234 static struct modldrv modldrv = { 235 &mod_driverops, "crash dump driver %I%", &dump_ops, 236 }; 237 238 static struct modlinkage modlinkage = { 239 MODREV_1, (void *)&modldrv, NULL 240 }; 241 242 int 243 _init(void) 244 { 245 return (mod_install(&modlinkage)); 246 } 247 248 int 249 _fini(void) 250 { 251 return (mod_remove(&modlinkage)); 252 } 253 254 int 255 _info(struct modinfo *modinfop) 256 { 257 return (mod_info(&modlinkage, modinfop)); 258 } 259