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 2007 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 * The driver portion of kmdb, which manages /dev/kmdb and passes requests along 30 * to the kmdb misc module (kmdbmod). 31 */ 32 33 #include <sys/conf.h> 34 #include <sys/stat.h> 35 #include <sys/modctl.h> 36 #include <sys/ddi.h> 37 #include <sys/sunddi.h> 38 #include <sys/open.h> 39 #include <sys/kobj.h> 40 #include <sys/kdi.h> 41 #include <sys/policy.h> 42 #include <sys/kobj_impl.h> 43 #include <sys/kmdb.h> 44 #include <sys/sysmacros.h> 45 #include <sys/consdev.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 #if defined(__x86) 94 if (cons_polledio == NULL) { 95 cmn_err(CE_NOTE, "kmdb not supported: no console polled I/O"); 96 return (ENOTSUP); 97 } 98 #endif 99 100 memsz = ddi_prop_get_int(DDI_DEV_T_ANY, kdrv_dip, 101 DDI_PROP_DONTPASS, "kmdb-memseg-size", 0); 102 103 for (flags = 0, i = 0; kdrv_flags_map[i].fm_name != NULL; i++) { 104 const kdrv_flags_map_t *fm = &kdrv_flags_map[i]; 105 if (ddi_prop_get_int(DDI_DEV_T_ANY, kdrv_dip, DDI_PROP_DONTPASS, 106 (char *)fm->fm_name, fm->fm_defval)) 107 flags |= fm->fm_flag; 108 } 109 110 cfg = kmem_alloc(KDRV_CFG_MAXLEN, KM_SLEEP); 111 112 if ((rc = copyinstr((caddr_t)arg, cfg, KDRV_CFG_MAXLEN, &got)) != 0) { 113 kmem_free(cfg, KDRV_CFG_MAXLEN); 114 return (rc == ENAMETOOLONG ? E2BIG : EFAULT); 115 } 116 117 rc = kctl_modload_activate(memsz, cfg, flags); 118 119 kmem_free(cfg, KDRV_CFG_MAXLEN); 120 121 return (rc); 122 } 123 124 static int 125 kdrv_deactivate(void) 126 { 127 return (kctl_deactivate()); 128 } 129 130 /*ARGSUSED*/ 131 static int 132 kdrv_ioctl(dev_t dev, int cmd, intptr_t arg, int flags, cred_t *credp, 133 int *rvalp) 134 { 135 switch (cmd) { 136 case KMDB_IOC_START: 137 return (kdrv_activate(arg)); 138 139 case KMDB_IOC_STOP: 140 return (kdrv_deactivate()); 141 142 default: 143 return (EINVAL); 144 } 145 } 146 147 /*ARGSUSED*/ 148 static int 149 kdrv_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 150 { 151 int error = DDI_SUCCESS; 152 153 switch (infocmd) { 154 case DDI_INFO_DEVT2DEVINFO: 155 *result = kdrv_dip; 156 break; 157 case DDI_INFO_DEVT2INSTANCE: 158 *result = (void *)(uintptr_t)getminor((dev_t)arg); 159 break; 160 default: 161 *result = NULL; 162 error = DDI_FAILURE; 163 } 164 165 return (error); 166 } 167 168 static int 169 kdrv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 170 { 171 if (cmd != DDI_ATTACH) 172 return (DDI_FAILURE); 173 174 if (ddi_create_minor_node(dip, ddi_get_name(dip), S_IFCHR, 175 ddi_get_instance(dip), DDI_PSEUDO, 0) != DDI_SUCCESS) 176 return (DDI_FAILURE); 177 178 kdrv_dip = dip; 179 180 if (kctl_attach(dip) != 0) 181 return (DDI_FAILURE); 182 183 return (DDI_SUCCESS); 184 } 185 186 static int 187 kdrv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 188 { 189 if (cmd != DDI_DETACH) 190 return (DDI_FAILURE); 191 192 if (kctl_detach() == EBUSY) 193 return (DDI_FAILURE); 194 195 ddi_remove_minor_node(dip, NULL); 196 197 return (DDI_SUCCESS); 198 } 199 200 static struct cb_ops kdrv_cb_ops = { 201 kdrv_open, 202 kdrv_close, 203 nodev, /* not a block driver */ 204 nodev, /* no print routine */ 205 nodev, /* no dump routine */ 206 nodev, /* no read routine */ 207 nodev, /* no write routine */ 208 kdrv_ioctl, 209 nodev, /* no devmap routine */ 210 nodev, /* no mmap routine */ 211 nodev, /* no segmap routine */ 212 nochpoll, /* no chpoll routine */ 213 ddi_prop_op, 214 0, /* not a STREAMS driver */ 215 D_NEW | D_MP, /* safe for multi-thread/multi-processor */ 216 }; 217 218 static struct dev_ops kdrv_ops = { 219 DEVO_REV, /* devo_rev */ 220 0, /* devo_refcnt */ 221 kdrv_getinfo, /* devo_getinfo */ 222 nulldev, /* devo_identify */ 223 nulldev, /* devo_probe */ 224 kdrv_attach, /* devo_attach */ 225 kdrv_detach, /* devo_detach */ 226 nodev, /* devo_reset */ 227 &kdrv_cb_ops, /* devo_cb_ops */ 228 (struct bus_ops *)0, /* devo_bus_ops */ 229 NULL, /* devo_power */ 230 }; 231 232 static struct modldrv modldrv = { 233 &mod_driverops, 234 "kmdb driver %I%", 235 &kdrv_ops 236 }; 237 238 static struct modlinkage modlinkage = { 239 MODREV_1, 240 (void *)&modldrv, 241 NULL 242 }; 243 244 int 245 _init(void) 246 { 247 return (mod_install(&modlinkage)); 248 } 249 250 int 251 _info(struct modinfo *modinfop) 252 { 253 return (mod_info(&modlinkage, modinfop)); 254 } 255 256 int 257 _fini(void) 258 { 259 return (mod_remove(&modlinkage)); 260 } 261