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 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 vnode_t *vp; 99 100 switch (cmd) { 101 case DIOCGETDUMPSIZE: 102 if (dump_conflags & DUMP_ALL) 103 size = ptob((uint64_t)physmem) / DUMP_COMPRESS_RATIO; 104 else { 105 /* 106 * We can't give a good answer for the DUMP_CURPROC 107 * because we won't know which process to use until it 108 * causes a panic. We'll therefore punt and give the 109 * caller the size for the kernel. 110 * 111 * This kernel size equation takes care of the 112 * boot time kernel footprint and also accounts 113 * for availrmem changes due to user explicit locking. 114 * Refer to common/vm/vm_page.c for an explanation 115 * of these counters. 116 */ 117 dumpsize_in_pages = (physinstalled - obp_pages - 118 availrmem - 119 anon_segkp_pages_locked - 120 k_anoninfo.ani_mem_resv - 121 pages_locked - 122 pages_claimed - 123 pages_useclaim); 124 125 /* 126 * Protect against vm vagaries. 127 */ 128 if (dumpsize_in_pages > (uint64_t)physmem) 129 dumpsize_in_pages = (uint64_t)physmem; 130 131 size = ptob(dumpsize_in_pages) / DUMP_COMPRESS_RATIO; 132 } 133 if (copyout(&size, (void *)arg, sizeof (size)) < 0) 134 error = EFAULT; 135 break; 136 137 case DIOCGETCONF: 138 mutex_enter(&dump_lock); 139 *rvalp = dump_conflags; 140 if (dumpvp && !(dumpvp->v_flag & VISSWAP)) 141 *rvalp |= DUMP_EXCL; 142 mutex_exit(&dump_lock); 143 break; 144 145 case DIOCSETCONF: 146 mutex_enter(&dump_lock); 147 if (arg == DUMP_KERNEL || arg == DUMP_ALL || 148 arg == DUMP_CURPROC) 149 dump_conflags = arg; 150 else 151 error = EINVAL; 152 mutex_exit(&dump_lock); 153 break; 154 155 case DIOCGETDEV: 156 mutex_enter(&dump_lock); 157 if (dumppath == NULL) { 158 mutex_exit(&dump_lock); 159 error = ENODEV; 160 break; 161 } 162 (void) strcpy(pathbuf, dumppath); 163 mutex_exit(&dump_lock); 164 error = copyoutstr(pathbuf, (void *)arg, MAXPATHLEN, NULL); 165 break; 166 167 case DIOCSETDEV: 168 case DIOCTRYDEV: 169 if ((error = copyinstr((char *)arg, pathbuf, MAXPATHLEN, 170 NULL)) != 0 || (error = lookupname(pathbuf, UIO_SYSSPACE, 171 FOLLOW, NULLVPP, &vp)) != 0) 172 break; 173 mutex_enter(&dump_lock); 174 if (vp->v_type == VBLK) 175 error = dumpinit(vp, pathbuf, cmd == DIOCTRYDEV); 176 else 177 error = ENOTBLK; 178 mutex_exit(&dump_lock); 179 VN_RELE(vp); 180 break; 181 182 case DIOCDUMP: 183 mutex_enter(&dump_lock); 184 if (dumpvp == NULL) 185 error = ENODEV; 186 else if (dumpvp->v_flag & VISSWAP) 187 error = EBUSY; 188 else 189 dumpsys(); 190 mutex_exit(&dump_lock); 191 break; 192 193 default: 194 error = ENXIO; 195 } 196 197 kmem_free(pathbuf, MAXPATHLEN); 198 return (error); 199 } 200 201 struct cb_ops dump_cb_ops = { 202 nulldev, /* open */ 203 nulldev, /* close */ 204 nodev, /* strategy */ 205 nodev, /* print */ 206 nodev, /* dump */ 207 nodev, /* read */ 208 nodev, /* write */ 209 dump_ioctl, /* ioctl */ 210 nodev, /* devmap */ 211 nodev, /* mmap */ 212 nodev, /* segmap */ 213 nochpoll, /* poll */ 214 ddi_prop_op, /* prop_op */ 215 0, /* streamtab */ 216 D_NEW|D_MP /* Driver compatibility flag */ 217 }; 218 219 struct dev_ops dump_ops = { 220 DEVO_REV, /* devo_rev, */ 221 0, /* refcnt */ 222 dump_info, /* info */ 223 nulldev, /* identify */ 224 nulldev, /* probe */ 225 dump_attach, /* attach */ 226 dump_detach, /* detach */ 227 nodev, /* reset */ 228 &dump_cb_ops, /* driver operations */ 229 (struct bus_ops *)0, /* bus operations */ 230 NULL, /* power */ 231 ddi_quiesce_not_needed, /* quiesce */ 232 }; 233 234 static struct modldrv modldrv = { 235 &mod_driverops, "crash dump driver", &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