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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * The driver portion of kmdb, which manages /dev/kmdb and passes requests along 31 * to the kmdb misc module (kmdbmod). 32 */ 33 34 #include <sys/conf.h> 35 #include <sys/stat.h> 36 #include <sys/modctl.h> 37 #include <sys/ddi.h> 38 #include <sys/sunddi.h> 39 #include <sys/open.h> 40 #include <sys/kobj.h> 41 #include <sys/kdi.h> 42 #include <sys/policy.h> 43 #include <sys/kobj_impl.h> 44 #include <sys/kmdb.h> 45 #include <sys/sysmacros.h> 46 47 #define KDRV_CFG_MAXLEN 2048 48 49 static dev_info_t *kdrv_dip; 50 51 /*ARGSUSED*/ 52 static int 53 kdrv_open(dev_t *dev, int openflags, int otyp, cred_t *credp) 54 { 55 if (otyp != OTYP_CHR) 56 return (EINVAL); 57 58 if (secpolicy_kmdb(credp) != 0) 59 return (EPERM); 60 61 return (0); 62 } 63 64 /*ARGSUSED*/ 65 static int 66 kdrv_close(dev_t dev, int openflags, int otyp, cred_t *credp) 67 { 68 return (0); 69 } 70 71 typedef struct kdrv_flags_map { 72 const char *fm_name; 73 int fm_defval; 74 uint_t fm_flag; 75 } kdrv_flags_map_t; 76 77 static const kdrv_flags_map_t kdrv_flags_map[] = { 78 { "kmdb-auto-entry", 1, KMDB_F_AUTO_ENTRY }, 79 { "kmdb-trap-noswitch", 0, KMDB_F_TRAP_NOSWITCH }, 80 { "kmdb-driver-debug", 0, KMDB_F_DRV_DEBUG }, 81 { NULL } 82 }; 83 84 static int 85 kdrv_activate(intptr_t arg) 86 { 87 uint_t flags; 88 size_t memsz; 89 char *cfg; 90 size_t got; 91 int i, rc; 92 93 memsz = ddi_prop_get_int(DDI_DEV_T_ANY, kdrv_dip, 94 DDI_PROP_DONTPASS, "kmdb-memseg-size", 0); 95 96 for (flags = 0, i = 0; kdrv_flags_map[i].fm_name != NULL; i++) { 97 const kdrv_flags_map_t *fm = &kdrv_flags_map[i]; 98 if (ddi_prop_get_int(DDI_DEV_T_ANY, kdrv_dip, DDI_PROP_DONTPASS, 99 (char *)fm->fm_name, fm->fm_defval)) 100 flags |= fm->fm_flag; 101 } 102 103 cfg = kmem_alloc(KDRV_CFG_MAXLEN, KM_SLEEP); 104 105 if ((rc = copyinstr((caddr_t)arg, cfg, KDRV_CFG_MAXLEN, &got)) != 0) { 106 kmem_free(cfg, KDRV_CFG_MAXLEN); 107 return (rc == ENAMETOOLONG ? E2BIG : EFAULT); 108 } 109 110 rc = kctl_modload_activate(memsz, cfg, flags); 111 112 kmem_free(cfg, KDRV_CFG_MAXLEN); 113 114 return (rc); 115 } 116 117 static int 118 kdrv_deactivate(void) 119 { 120 return (kctl_deactivate()); 121 } 122 123 /*ARGSUSED*/ 124 static int 125 kdrv_ioctl(dev_t dev, int cmd, intptr_t arg, int flags, cred_t *credp, 126 int *rvalp) 127 { 128 switch (cmd) { 129 case KMDB_IOC_START: 130 return (kdrv_activate(arg)); 131 132 case KMDB_IOC_STOP: 133 return (kdrv_deactivate()); 134 135 default: 136 return (EINVAL); 137 } 138 } 139 140 /*ARGSUSED*/ 141 static int 142 kdrv_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 143 { 144 int error = DDI_SUCCESS; 145 146 switch (infocmd) { 147 case DDI_INFO_DEVT2DEVINFO: 148 *result = kdrv_dip; 149 break; 150 case DDI_INFO_DEVT2INSTANCE: 151 *result = (void *)(uintptr_t)getminor((dev_t)arg); 152 break; 153 default: 154 *result = NULL; 155 error = DDI_FAILURE; 156 } 157 158 return (error); 159 } 160 161 static int 162 kdrv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 163 { 164 if (cmd != DDI_ATTACH) 165 return (DDI_FAILURE); 166 167 if (ddi_create_minor_node(dip, ddi_get_name(dip), S_IFCHR, 168 ddi_get_instance(dip), DDI_PSEUDO, 0) != DDI_SUCCESS) 169 return (DDI_FAILURE); 170 171 kdrv_dip = dip; 172 173 if (kctl_attach(dip) != 0) 174 return (DDI_FAILURE); 175 176 return (DDI_SUCCESS); 177 } 178 179 static int 180 kdrv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 181 { 182 if (cmd != DDI_DETACH) 183 return (DDI_FAILURE); 184 185 if (kctl_detach() == EBUSY) 186 return (DDI_FAILURE); 187 188 ddi_remove_minor_node(dip, NULL); 189 190 return (DDI_SUCCESS); 191 } 192 193 static struct cb_ops kdrv_cb_ops = { 194 kdrv_open, 195 kdrv_close, 196 nodev, /* not a block driver */ 197 nodev, /* no print routine */ 198 nodev, /* no dump routine */ 199 nodev, /* no read routine */ 200 nodev, /* no write routine */ 201 kdrv_ioctl, 202 nodev, /* no devmap routine */ 203 nodev, /* no mmap routine */ 204 nodev, /* no segmap routine */ 205 nochpoll, /* no chpoll routine */ 206 ddi_prop_op, 207 0, /* not a STREAMS driver */ 208 D_NEW | D_MP, /* safe for multi-thread/multi-processor */ 209 }; 210 211 static struct dev_ops kdrv_ops = { 212 DEVO_REV, /* devo_rev */ 213 0, /* devo_refcnt */ 214 kdrv_getinfo, /* devo_getinfo */ 215 nulldev, /* devo_identify */ 216 nulldev, /* devo_probe */ 217 kdrv_attach, /* devo_attach */ 218 kdrv_detach, /* devo_detach */ 219 nodev, /* devo_reset */ 220 &kdrv_cb_ops, /* devo_cb_ops */ 221 (struct bus_ops *)0, /* devo_bus_ops */ 222 NULL, /* devo_power */ 223 }; 224 225 static struct modldrv modldrv = { 226 &mod_driverops, 227 "kmdb driver %I%", 228 &kdrv_ops 229 }; 230 231 static struct modlinkage modlinkage = { 232 MODREV_1, 233 (void *)&modldrv, 234 NULL 235 }; 236 237 int 238 _init(void) 239 { 240 return (mod_install(&modlinkage)); 241 } 242 243 int 244 _info(struct modinfo *modinfop) 245 { 246 return (mod_info(&modlinkage, modinfop)); 247 } 248 249 int 250 _fini(void) 251 { 252 return (mod_remove(&modlinkage)); 253 } 254