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 */ 24 25 26 /* 27 * Dump driver. Provides ioctls to get/set crash dump configuration. 28 */ 29 30 #include <sys/types.h> 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/vnode.h> 34 #include <sys/uio.h> 35 #include <sys/cred.h> 36 #include <sys/kmem.h> 37 #include <sys/errno.h> 38 #include <sys/modctl.h> 39 #include <sys/dumphdr.h> 40 #include <sys/dumpadm.h> 41 #include <sys/pathname.h> 42 #include <sys/file.h> 43 #include <vm/anon.h> 44 #include <sys/stat.h> 45 #include <sys/conf.h> 46 #include <sys/ddi.h> 47 #include <sys/sunddi.h> 48 49 static dev_info_t *dump_devi; 50 51 static int 52 dump_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 53 { 54 if (cmd != DDI_ATTACH) 55 return (DDI_FAILURE); 56 if (ddi_create_minor_node(devi, "dump", S_IFCHR, 0, DDI_PSEUDO, NULL) == 57 DDI_FAILURE) { 58 ddi_remove_minor_node(devi, NULL); 59 return (DDI_FAILURE); 60 } 61 dump_devi = devi; 62 return (DDI_SUCCESS); 63 } 64 65 static int 66 dump_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 67 { 68 if (cmd != DDI_DETACH) 69 return (DDI_FAILURE); 70 ddi_remove_minor_node(devi, NULL); 71 return (DDI_SUCCESS); 72 } 73 74 /*ARGSUSED*/ 75 static int 76 dump_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 77 { 78 switch (infocmd) { 79 case DDI_INFO_DEVT2DEVINFO: 80 *result = dump_devi; 81 return (DDI_SUCCESS); 82 case DDI_INFO_DEVT2INSTANCE: 83 *result = 0; 84 return (DDI_SUCCESS); 85 } 86 return (DDI_FAILURE); 87 } 88 89 /*ARGSUSED*/ 90 int 91 dump_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp) 92 { 93 uint64_t size; 94 uint64_t dumpsize_in_pages; 95 int error = 0; 96 char *pathbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 97 char uuidbuf[36 + 1]; 98 size_t len; 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 pages_locked - 123 pages_claimed - 124 pages_useclaim); 125 126 /* 127 * Protect against vm vagaries. 128 */ 129 if (dumpsize_in_pages > (uint64_t)physmem) 130 dumpsize_in_pages = (uint64_t)physmem; 131 132 size = ptob(dumpsize_in_pages) / DUMP_COMPRESS_RATIO; 133 } 134 if (copyout(&size, (void *)arg, sizeof (size)) < 0) 135 error = EFAULT; 136 break; 137 138 case DIOCGETCONF: 139 mutex_enter(&dump_lock); 140 *rvalp = dump_conflags; 141 if (dumpvp && !(dumpvp->v_flag & VISSWAP)) 142 *rvalp |= DUMP_EXCL; 143 mutex_exit(&dump_lock); 144 break; 145 146 case DIOCSETCONF: 147 mutex_enter(&dump_lock); 148 if (arg == DUMP_KERNEL || arg == DUMP_ALL || 149 arg == DUMP_CURPROC) 150 dump_conflags = arg; 151 else 152 error = EINVAL; 153 mutex_exit(&dump_lock); 154 break; 155 156 case DIOCGETDEV: 157 mutex_enter(&dump_lock); 158 if (dumppath == NULL) { 159 mutex_exit(&dump_lock); 160 error = ENODEV; 161 break; 162 } 163 (void) strcpy(pathbuf, dumppath); 164 mutex_exit(&dump_lock); 165 error = copyoutstr(pathbuf, (void *)arg, MAXPATHLEN, NULL); 166 break; 167 168 case DIOCSETDEV: 169 case DIOCTRYDEV: 170 if ((error = copyinstr((char *)arg, pathbuf, MAXPATHLEN, 171 NULL)) != 0 || (error = lookupname(pathbuf, UIO_SYSSPACE, 172 FOLLOW, NULLVPP, &vp)) != 0) 173 break; 174 mutex_enter(&dump_lock); 175 if (vp->v_type == VBLK) 176 error = dumpinit(vp, pathbuf, cmd == DIOCTRYDEV); 177 else 178 error = ENOTBLK; 179 mutex_exit(&dump_lock); 180 VN_RELE(vp); 181 break; 182 183 case DIOCDUMP: 184 mutex_enter(&dump_lock); 185 if (dumpvp == NULL) 186 error = ENODEV; 187 else if (dumpvp->v_flag & VISSWAP) 188 error = EBUSY; 189 else 190 dumpsys(); 191 mutex_exit(&dump_lock); 192 break; 193 194 case DIOCSETUUID: 195 if ((error = copyinstr((char *)arg, uuidbuf, sizeof (uuidbuf), 196 &len)) != 0) 197 break; 198 199 if (len != 37) { 200 error = EINVAL; 201 break; 202 } 203 204 error = dump_set_uuid(uuidbuf); 205 break; 206 207 case DIOCGETUUID: 208 error = copyoutstr(dump_get_uuid(), (void *)arg, 37, NULL); 209 break; 210 211 default: 212 error = ENXIO; 213 } 214 215 kmem_free(pathbuf, MAXPATHLEN); 216 return (error); 217 } 218 219 struct cb_ops dump_cb_ops = { 220 nulldev, /* open */ 221 nulldev, /* close */ 222 nodev, /* strategy */ 223 nodev, /* print */ 224 nodev, /* dump */ 225 nodev, /* read */ 226 nodev, /* write */ 227 dump_ioctl, /* ioctl */ 228 nodev, /* devmap */ 229 nodev, /* mmap */ 230 nodev, /* segmap */ 231 nochpoll, /* poll */ 232 ddi_prop_op, /* prop_op */ 233 0, /* streamtab */ 234 D_NEW|D_MP /* Driver compatibility flag */ 235 }; 236 237 struct dev_ops dump_ops = { 238 DEVO_REV, /* devo_rev, */ 239 0, /* refcnt */ 240 dump_info, /* info */ 241 nulldev, /* identify */ 242 nulldev, /* probe */ 243 dump_attach, /* attach */ 244 dump_detach, /* detach */ 245 nodev, /* reset */ 246 &dump_cb_ops, /* driver operations */ 247 (struct bus_ops *)0, /* bus operations */ 248 NULL, /* power */ 249 ddi_quiesce_not_needed, /* quiesce */ 250 }; 251 252 static struct modldrv modldrv = { 253 &mod_driverops, "crash dump driver", &dump_ops, 254 }; 255 256 static struct modlinkage modlinkage = { 257 MODREV_1, (void *)&modldrv, NULL 258 }; 259 260 int 261 _init(void) 262 { 263 return (mod_install(&modlinkage)); 264 } 265 266 int 267 _fini(void) 268 { 269 return (mod_remove(&modlinkage)); 270 } 271 272 int 273 _info(struct modinfo *modinfop) 274 { 275 return (mod_info(&modlinkage, modinfop)); 276 } 277