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 * The driver portion of kmdb, which manages /dev/kmdb and passes requests along 29 * to the kmdb misc module (kmdbmod). 30 */ 31 32 #include <sys/conf.h> 33 #include <sys/stat.h> 34 #include <sys/modctl.h> 35 #include <sys/ddi.h> 36 #include <sys/sunddi.h> 37 #include <sys/open.h> 38 #include <sys/kobj.h> 39 #include <sys/kdi.h> 40 #include <sys/policy.h> 41 #include <sys/kobj_impl.h> 42 #include <sys/kmdb.h> 43 #include <sys/sysmacros.h> 44 #include <sys/consdev.h> 45 46 #define KDRV_CFG_MAXLEN 2048 47 48 static dev_info_t *kdrv_dip; 49 50 /*ARGSUSED*/ 51 static int 52 kdrv_open(dev_t *dev, int openflags, int otyp, cred_t *credp) 53 { 54 if (otyp != OTYP_CHR) 55 return (EINVAL); 56 57 if (secpolicy_kmdb(credp) != 0) 58 return (EPERM); 59 60 return (0); 61 } 62 63 /*ARGSUSED*/ 64 static int 65 kdrv_close(dev_t dev, int openflags, int otyp, cred_t *credp) 66 { 67 return (0); 68 } 69 70 typedef struct kdrv_flags_map { 71 const char *fm_name; 72 int fm_defval; 73 uint_t fm_flag; 74 } kdrv_flags_map_t; 75 76 static const kdrv_flags_map_t kdrv_flags_map[] = { 77 { "kmdb-auto-entry", 1, KMDB_F_AUTO_ENTRY }, 78 { "kmdb-trap-noswitch", 0, KMDB_F_TRAP_NOSWITCH }, 79 { "kmdb-driver-debug", 0, KMDB_F_DRV_DEBUG }, 80 { NULL } 81 }; 82 83 static int 84 kdrv_activate(intptr_t arg) 85 { 86 uint_t flags; 87 size_t memsz; 88 char *cfg; 89 size_t got; 90 int i, rc; 91 92 #if defined(__x86) 93 if (cons_polledio == NULL) { 94 cmn_err(CE_NOTE, "kmdb not supported: no console polled I/O"); 95 return (ENOTSUP); 96 } 97 #endif 98 99 memsz = ddi_prop_get_int(DDI_DEV_T_ANY, kdrv_dip, 100 DDI_PROP_DONTPASS, "kmdb-memseg-size", 0); 101 102 for (flags = 0, i = 0; kdrv_flags_map[i].fm_name != NULL; i++) { 103 const kdrv_flags_map_t *fm = &kdrv_flags_map[i]; 104 if (ddi_prop_get_int(DDI_DEV_T_ANY, kdrv_dip, DDI_PROP_DONTPASS, 105 (char *)fm->fm_name, fm->fm_defval)) 106 flags |= fm->fm_flag; 107 } 108 109 cfg = kmem_alloc(KDRV_CFG_MAXLEN, KM_SLEEP); 110 111 if ((rc = copyinstr((caddr_t)arg, cfg, KDRV_CFG_MAXLEN, &got)) != 0) { 112 kmem_free(cfg, KDRV_CFG_MAXLEN); 113 return (rc == ENAMETOOLONG ? E2BIG : EFAULT); 114 } 115 116 rc = kctl_modload_activate(memsz, cfg, flags); 117 118 kmem_free(cfg, KDRV_CFG_MAXLEN); 119 120 return (rc); 121 } 122 123 static int 124 kdrv_deactivate(void) 125 { 126 return (kctl_deactivate()); 127 } 128 129 /*ARGSUSED*/ 130 static int 131 kdrv_ioctl(dev_t dev, int cmd, intptr_t arg, int flags, cred_t *credp, 132 int *rvalp) 133 { 134 switch (cmd) { 135 case KMDB_IOC_START: 136 return (kdrv_activate(arg)); 137 138 case KMDB_IOC_STOP: 139 return (kdrv_deactivate()); 140 141 default: 142 return (EINVAL); 143 } 144 } 145 146 /*ARGSUSED*/ 147 static int 148 kdrv_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 149 { 150 int error = DDI_SUCCESS; 151 152 switch (infocmd) { 153 case DDI_INFO_DEVT2DEVINFO: 154 *result = kdrv_dip; 155 break; 156 case DDI_INFO_DEVT2INSTANCE: 157 *result = (void *)(uintptr_t)getminor((dev_t)arg); 158 break; 159 default: 160 *result = NULL; 161 error = DDI_FAILURE; 162 } 163 164 return (error); 165 } 166 167 static int 168 kdrv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 169 { 170 if (cmd != DDI_ATTACH) 171 return (DDI_FAILURE); 172 173 if (ddi_create_minor_node(dip, ddi_get_name(dip), S_IFCHR, 174 ddi_get_instance(dip), DDI_PSEUDO, 0) != DDI_SUCCESS) 175 return (DDI_FAILURE); 176 177 kdrv_dip = dip; 178 179 if (kctl_attach(dip) != 0) 180 return (DDI_FAILURE); 181 182 return (DDI_SUCCESS); 183 } 184 185 static int 186 kdrv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 187 { 188 if (cmd != DDI_DETACH) 189 return (DDI_FAILURE); 190 191 if (kctl_detach() == EBUSY) 192 return (DDI_FAILURE); 193 194 ddi_remove_minor_node(dip, NULL); 195 196 return (DDI_SUCCESS); 197 } 198 199 static struct cb_ops kdrv_cb_ops = { 200 kdrv_open, 201 kdrv_close, 202 nodev, /* not a block driver */ 203 nodev, /* no print routine */ 204 nodev, /* no dump routine */ 205 nodev, /* no read routine */ 206 nodev, /* no write routine */ 207 kdrv_ioctl, 208 nodev, /* no devmap routine */ 209 nodev, /* no mmap routine */ 210 nodev, /* no segmap routine */ 211 nochpoll, /* no chpoll routine */ 212 ddi_prop_op, 213 0, /* not a STREAMS driver */ 214 D_NEW | D_MP, /* safe for multi-thread/multi-processor */ 215 }; 216 217 static struct dev_ops kdrv_ops = { 218 DEVO_REV, /* devo_rev */ 219 0, /* devo_refcnt */ 220 kdrv_getinfo, /* devo_getinfo */ 221 nulldev, /* devo_identify */ 222 nulldev, /* devo_probe */ 223 kdrv_attach, /* devo_attach */ 224 kdrv_detach, /* devo_detach */ 225 nodev, /* devo_reset */ 226 &kdrv_cb_ops, /* devo_cb_ops */ 227 (struct bus_ops *)0, /* devo_bus_ops */ 228 NULL, /* devo_power */ 229 ddi_quiesce_not_needed, /* devo_quiesce */ 230 }; 231 232 static struct modldrv modldrv = { 233 &mod_driverops, 234 "kmdb driver", 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 }