stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 1 | /* |
| 2 | * CDDL HEADER START |
| 3 | * |
| 4 | * The contents of this file are subject to the terms of the |
dilpreet | 00d0963 | 2006-04-23 15:26:28 -0700 | [diff] [blame] | 5 | * Common Development and Distribution License (the "License"). |
| 6 | * You may not use this file except in compliance with the License. |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 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 | /* |
Stephen Hanson | 8cffa12 | 2009-03-18 05:54:25 -0700 | [diff] [blame] | 22 | * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 23 | * Use is subject to license terms. |
| 24 | */ |
Garrett D'Amore | cd21e7c | 2012-03-11 22:00:47 -0700 | [diff] [blame] | 25 | /* |
| 26 | * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved. |
| 27 | */ |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 28 | |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 29 | |
| 30 | #include <sys/types.h> |
| 31 | #include <sys/sysmacros.h> |
| 32 | #include <sys/buf.h> |
| 33 | #include <sys/errno.h> |
| 34 | #include <sys/modctl.h> |
| 35 | #include <sys/conf.h> |
| 36 | #include <sys/stat.h> |
| 37 | #include <sys/kmem.h> |
| 38 | #include <sys/proc.h> |
| 39 | #include <sys/cpuvar.h> |
| 40 | #include <sys/ddi_impldefs.h> |
| 41 | #include <sys/ddi.h> |
dilpreet | 00d0963 | 2006-04-23 15:26:28 -0700 | [diff] [blame] | 42 | #include <sys/fm/protocol.h> |
| 43 | #include <sys/fm/util.h> |
| 44 | #include <sys/fm/io/ddi.h> |
| 45 | #include <sys/sysevent/eventdefs.h> |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 46 | #include <sys/sunddi.h> |
| 47 | #include <sys/sunndi.h> |
| 48 | #include <sys/debug.h> |
| 49 | #include <sys/bofi.h> |
| 50 | #include <sys/dvma.h> |
| 51 | #include <sys/bofi_impl.h> |
| 52 | |
| 53 | /* |
| 54 | * Testing the resilience of a hardened device driver requires a suitably wide |
| 55 | * range of different types of "typical" hardware faults to be injected, |
| 56 | * preferably in a controlled and repeatable fashion. This is not in general |
| 57 | * possible via hardware, so the "fault injection test harness" is provided. |
| 58 | * This works by intercepting calls from the driver to various DDI routines, |
| 59 | * and then corrupting the result of those DDI routine calls as if the |
| 60 | * hardware had caused the corruption. |
| 61 | * |
| 62 | * Conceptually, the bofi driver consists of two parts: |
| 63 | * |
| 64 | * A driver interface that supports a number of ioctls which allow error |
| 65 | * definitions ("errdefs") to be defined and subsequently managed. The |
| 66 | * driver is a clone driver, so each open will create a separate |
| 67 | * invocation. Any errdefs created by using ioctls to that invocation |
| 68 | * will automatically be deleted when that invocation is closed. |
| 69 | * |
| 70 | * Intercept routines: When the bofi driver is attached, it edits the |
| 71 | * bus_ops structure of the bus nexus specified by the "bofi-nexus" |
| 72 | * field in the "bofi.conf" file, thus allowing the |
| 73 | * bofi driver to intercept various ddi functions. These intercept |
| 74 | * routines primarily carry out fault injections based on the errdefs |
| 75 | * created for that device. |
| 76 | * |
| 77 | * Faults can be injected into: |
| 78 | * |
| 79 | * DMA (corrupting data for DMA to/from memory areas defined by |
| 80 | * ddi_dma_setup(), ddi_dma_bind_handle(), etc) |
| 81 | * |
| 82 | * Physical IO (corrupting data sent/received via ddi_get8(), ddi_put8(), |
| 83 | * etc), |
| 84 | * |
| 85 | * Interrupts (generating spurious interrupts, losing interrupts, |
| 86 | * delaying interrupts). |
| 87 | * |
| 88 | * By default, ddi routines called from all drivers will be intercepted |
| 89 | * and faults potentially injected. However, the "bofi-to-test" field in |
| 90 | * the "bofi.conf" file can be set to a space-separated list of drivers to |
| 91 | * test (or by preceding each driver name in the list with an "!", a list |
| 92 | * of drivers not to test). |
| 93 | * |
| 94 | * In addition to fault injection, the bofi driver does a number of static |
| 95 | * checks which are controlled by properties in the "bofi.conf" file. |
| 96 | * |
| 97 | * "bofi-ddi-check" - if set will validate that there are no PIO access |
| 98 | * other than those using the DDI routines (ddi_get8(), ddi_put8(), etc). |
| 99 | * |
| 100 | * "bofi-range-check" - if set to values 1 (warning) or 2 (panic), will |
| 101 | * validate that calls to ddi_get8(), ddi_put8(), etc are not made |
| 102 | * specifying addresses outside the range of the access_handle. |
| 103 | * |
| 104 | * "bofi-sync-check" - if set will validate that calls to ddi_dma_sync() |
| 105 | * are being made correctly. |
| 106 | */ |
| 107 | |
| 108 | extern void *bp_mapin_common(struct buf *, int); |
| 109 | |
| 110 | static int bofi_ddi_check; |
| 111 | static int bofi_sync_check; |
| 112 | static int bofi_range_check; |
| 113 | |
| 114 | static struct bofi_link bofi_link_array[BOFI_NLINKS], *bofi_link_freelist; |
| 115 | |
| 116 | #define LLSZMASK (sizeof (uint64_t)-1) |
| 117 | |
| 118 | #define HDL_HASH_TBL_SIZE 64 |
| 119 | static struct bofi_shadow hhash_table[HDL_HASH_TBL_SIZE]; |
| 120 | static struct bofi_shadow dhash_table[HDL_HASH_TBL_SIZE]; |
| 121 | #define HDL_DHASH(x) \ |
| 122 | (&dhash_table[((uintptr_t)(x) >> 3) & (HDL_HASH_TBL_SIZE-1)]) |
| 123 | #define HDL_HHASH(x) \ |
| 124 | (&hhash_table[((uintptr_t)(x) >> 5) & (HDL_HASH_TBL_SIZE-1)]) |
| 125 | |
| 126 | static struct bofi_shadow shadow_list; |
| 127 | static struct bofi_errent *errent_listp; |
| 128 | |
| 129 | static char driver_list[NAMESIZE]; |
| 130 | static int driver_list_size; |
| 131 | static int driver_list_neg; |
| 132 | static char nexus_name[NAMESIZE]; |
| 133 | |
| 134 | static int initialized = 0; |
| 135 | |
stephh | e5ba14f | 2007-10-09 15:55:12 -0700 | [diff] [blame] | 136 | #define NCLONES 2560 |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 137 | static int clone_tab[NCLONES]; |
| 138 | |
| 139 | static dev_info_t *our_dip; |
| 140 | |
| 141 | static kmutex_t bofi_mutex; |
| 142 | static kmutex_t clone_tab_mutex; |
| 143 | static kmutex_t bofi_low_mutex; |
| 144 | static ddi_iblock_cookie_t bofi_low_cookie; |
| 145 | static uint_t bofi_signal(caddr_t arg); |
| 146 | static int bofi_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); |
| 147 | static int bofi_attach(dev_info_t *, ddi_attach_cmd_t); |
| 148 | static int bofi_detach(dev_info_t *, ddi_detach_cmd_t); |
| 149 | static int bofi_open(dev_t *, int, int, cred_t *); |
| 150 | static int bofi_close(dev_t, int, int, cred_t *); |
| 151 | static int bofi_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); |
| 152 | static int bofi_errdef_alloc(struct bofi_errdef *, char *, |
| 153 | struct bofi_errent *); |
| 154 | static int bofi_errdef_free(struct bofi_errent *); |
| 155 | static void bofi_start(struct bofi_errctl *, char *); |
| 156 | static void bofi_stop(struct bofi_errctl *, char *); |
| 157 | static void bofi_broadcast(struct bofi_errctl *, char *); |
| 158 | static void bofi_clear_acc_chk(struct bofi_errctl *, char *); |
| 159 | static void bofi_clear_errors(struct bofi_errctl *, char *); |
| 160 | static void bofi_clear_errdefs(struct bofi_errctl *, char *); |
| 161 | static int bofi_errdef_check(struct bofi_errstate *, |
| 162 | struct acc_log_elem **); |
| 163 | static int bofi_errdef_check_w(struct bofi_errstate *, |
| 164 | struct acc_log_elem **); |
| 165 | static int bofi_map(dev_info_t *, dev_info_t *, ddi_map_req_t *, |
| 166 | off_t, off_t, caddr_t *); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 167 | static int bofi_dma_allochdl(dev_info_t *, dev_info_t *, |
| 168 | ddi_dma_attr_t *, int (*)(caddr_t), caddr_t, |
| 169 | ddi_dma_handle_t *); |
| 170 | static int bofi_dma_freehdl(dev_info_t *, dev_info_t *, |
| 171 | ddi_dma_handle_t); |
| 172 | static int bofi_dma_bindhdl(dev_info_t *, dev_info_t *, |
| 173 | ddi_dma_handle_t, struct ddi_dma_req *, ddi_dma_cookie_t *, |
| 174 | uint_t *); |
| 175 | static int bofi_dma_unbindhdl(dev_info_t *, dev_info_t *, |
| 176 | ddi_dma_handle_t); |
| 177 | static int bofi_dma_flush(dev_info_t *, dev_info_t *, ddi_dma_handle_t, |
| 178 | off_t, size_t, uint_t); |
| 179 | static int bofi_dma_ctl(dev_info_t *, dev_info_t *, ddi_dma_handle_t, |
| 180 | enum ddi_dma_ctlops, off_t *, size_t *, caddr_t *, uint_t); |
| 181 | static int bofi_dma_win(dev_info_t *, dev_info_t *, ddi_dma_handle_t, |
| 182 | uint_t, off_t *, size_t *, ddi_dma_cookie_t *, uint_t *); |
| 183 | static int bofi_intr_ops(dev_info_t *dip, dev_info_t *rdip, |
| 184 | ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, |
| 185 | void *result); |
dilpreet | 00d0963 | 2006-04-23 15:26:28 -0700 | [diff] [blame] | 186 | static int bofi_fm_ereport_callback(sysevent_t *ev, void *cookie); |
| 187 | |
| 188 | evchan_t *bofi_error_chan; |
| 189 | |
| 190 | #define FM_SIMULATED_DMA "simulated.dma" |
| 191 | #define FM_SIMULATED_PIO "simulated.pio" |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 192 | |
| 193 | #if defined(__sparc) |
| 194 | static void bofi_dvma_kaddr_load(ddi_dma_handle_t, caddr_t, uint_t, |
| 195 | uint_t, ddi_dma_cookie_t *); |
| 196 | static void bofi_dvma_unload(ddi_dma_handle_t, uint_t, uint_t); |
| 197 | static void bofi_dvma_sync(ddi_dma_handle_t, uint_t, uint_t); |
| 198 | static void bofi_dvma_reserve(dev_info_t *, ddi_dma_handle_t); |
| 199 | #endif |
| 200 | static int driver_under_test(dev_info_t *); |
| 201 | static int bofi_check_acc_hdl(ddi_acc_impl_t *); |
| 202 | static int bofi_check_dma_hdl(ddi_dma_impl_t *); |
| 203 | static int bofi_post_event(dev_info_t *dip, dev_info_t *rdip, |
| 204 | ddi_eventcookie_t eventhdl, void *impl_data); |
| 205 | |
| 206 | static struct bus_ops bofi_bus_ops = { |
| 207 | BUSO_REV, |
| 208 | bofi_map, |
| 209 | NULL, |
| 210 | NULL, |
| 211 | NULL, |
| 212 | i_ddi_map_fault, |
Garrett D'Amore | cd21e7c | 2012-03-11 22:00:47 -0700 | [diff] [blame] | 213 | NULL, |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 214 | bofi_dma_allochdl, |
| 215 | bofi_dma_freehdl, |
| 216 | bofi_dma_bindhdl, |
| 217 | bofi_dma_unbindhdl, |
| 218 | bofi_dma_flush, |
| 219 | bofi_dma_win, |
| 220 | bofi_dma_ctl, |
| 221 | NULL, |
| 222 | ddi_bus_prop_op, |
| 223 | ndi_busop_get_eventcookie, |
| 224 | ndi_busop_add_eventcall, |
| 225 | ndi_busop_remove_eventcall, |
| 226 | bofi_post_event, |
| 227 | NULL, |
| 228 | 0, |
| 229 | 0, |
| 230 | 0, |
| 231 | 0, |
| 232 | 0, |
| 233 | 0, |
| 234 | 0, |
| 235 | bofi_intr_ops |
| 236 | }; |
| 237 | |
| 238 | static struct cb_ops bofi_cb_ops = { |
cth | a913d55 | 2005-07-29 08:45:16 -0700 | [diff] [blame] | 239 | bofi_open, /* open */ |
| 240 | bofi_close, /* close */ |
| 241 | nodev, /* strategy */ |
| 242 | nodev, /* print */ |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 243 | nodev, /* dump */ |
cth | a913d55 | 2005-07-29 08:45:16 -0700 | [diff] [blame] | 244 | nodev, /* read */ |
| 245 | nodev, /* write */ |
| 246 | bofi_ioctl, /* ioctl */ |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 247 | nodev, /* devmap */ |
cth | a913d55 | 2005-07-29 08:45:16 -0700 | [diff] [blame] | 248 | nodev, /* mmap */ |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 249 | nodev, /* segmap */ |
cth | a913d55 | 2005-07-29 08:45:16 -0700 | [diff] [blame] | 250 | nochpoll, /* chpoll */ |
| 251 | ddi_prop_op, /* prop_op */ |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 252 | NULL, /* for STREAMS drivers */ |
cth | a913d55 | 2005-07-29 08:45:16 -0700 | [diff] [blame] | 253 | D_MP, /* driver compatibility flag */ |
| 254 | CB_REV, /* cb_ops revision */ |
| 255 | nodev, /* aread */ |
| 256 | nodev /* awrite */ |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 257 | }; |
| 258 | |
| 259 | static struct dev_ops bofi_ops = { |
| 260 | DEVO_REV, /* driver build version */ |
| 261 | 0, /* device reference count */ |
| 262 | bofi_getinfo, |
| 263 | nulldev, |
| 264 | nulldev, /* probe */ |
| 265 | bofi_attach, |
| 266 | bofi_detach, |
| 267 | nulldev, /* reset */ |
| 268 | &bofi_cb_ops, |
| 269 | (struct bus_ops *)NULL, |
Sherry Moore | 1939740 | 2008-09-22 16:30:26 -0700 | [diff] [blame] | 270 | nulldev, /* power */ |
| 271 | ddi_quiesce_not_needed, /* quiesce */ |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 272 | }; |
| 273 | |
| 274 | /* module configuration stuff */ |
| 275 | static void *statep; |
| 276 | |
| 277 | static struct modldrv modldrv = { |
| 278 | &mod_driverops, |
Sherry Moore | 1939740 | 2008-09-22 16:30:26 -0700 | [diff] [blame] | 279 | "bofi driver", |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 280 | &bofi_ops |
| 281 | }; |
| 282 | |
| 283 | static struct modlinkage modlinkage = { |
| 284 | MODREV_1, |
| 285 | &modldrv, |
| 286 | 0 |
| 287 | }; |
| 288 | |
| 289 | static struct bus_ops save_bus_ops; |
| 290 | |
| 291 | #if defined(__sparc) |
| 292 | static struct dvma_ops bofi_dvma_ops = { |
| 293 | DVMAO_REV, |
| 294 | bofi_dvma_kaddr_load, |
| 295 | bofi_dvma_unload, |
| 296 | bofi_dvma_sync |
| 297 | }; |
| 298 | #endif |
| 299 | |
| 300 | /* |
| 301 | * support routine - map user page into kernel virtual |
| 302 | */ |
| 303 | static caddr_t |
| 304 | dmareq_mapin(offset_t len, caddr_t addr, struct as *as, int flag) |
| 305 | { |
| 306 | struct buf buf; |
| 307 | struct proc proc; |
| 308 | |
| 309 | /* |
| 310 | * mock up a buf structure so we can call bp_mapin_common() |
| 311 | */ |
| 312 | buf.b_flags = B_PHYS; |
| 313 | buf.b_un.b_addr = (caddr_t)addr; |
| 314 | buf.b_bcount = (size_t)len; |
| 315 | proc.p_as = as; |
| 316 | buf.b_proc = &proc; |
| 317 | return (bp_mapin_common(&buf, flag)); |
| 318 | } |
| 319 | |
| 320 | |
| 321 | /* |
| 322 | * support routine - map page chain into kernel virtual |
| 323 | */ |
| 324 | static caddr_t |
| 325 | dmareq_pp_mapin(offset_t len, uint_t offset, page_t *pp, int flag) |
| 326 | { |
| 327 | struct buf buf; |
| 328 | |
| 329 | /* |
| 330 | * mock up a buf structure so we can call bp_mapin_common() |
| 331 | */ |
| 332 | buf.b_flags = B_PAGEIO; |
| 333 | buf.b_un.b_addr = (caddr_t)(uintptr_t)offset; |
| 334 | buf.b_bcount = (size_t)len; |
| 335 | buf.b_pages = pp; |
| 336 | return (bp_mapin_common(&buf, flag)); |
| 337 | } |
| 338 | |
| 339 | |
| 340 | /* |
| 341 | * support routine - map page array into kernel virtual |
| 342 | */ |
| 343 | static caddr_t |
| 344 | dmareq_pplist_mapin(uint_t len, caddr_t addr, page_t **pplist, struct as *as, |
| 345 | int flag) |
| 346 | { |
| 347 | struct buf buf; |
| 348 | struct proc proc; |
| 349 | |
| 350 | /* |
| 351 | * mock up a buf structure so we can call bp_mapin_common() |
| 352 | */ |
| 353 | buf.b_flags = B_PHYS|B_SHADOW; |
| 354 | buf.b_un.b_addr = addr; |
| 355 | buf.b_bcount = len; |
| 356 | buf.b_shadow = pplist; |
| 357 | proc.p_as = as; |
| 358 | buf.b_proc = &proc; |
| 359 | return (bp_mapin_common(&buf, flag)); |
| 360 | } |
| 361 | |
| 362 | |
| 363 | /* |
| 364 | * support routine - map dmareq into kernel virtual if not already |
| 365 | * fills in *lenp with length |
| 366 | * *mapaddr will be new kernel virtual address - or null if no mapping needed |
| 367 | */ |
| 368 | static caddr_t |
| 369 | ddi_dmareq_mapin(struct ddi_dma_req *dmareqp, caddr_t *mapaddrp, |
| 370 | offset_t *lenp) |
| 371 | { |
| 372 | int sleep = (dmareqp->dmar_fp == DDI_DMA_SLEEP) ? VM_SLEEP: VM_NOSLEEP; |
| 373 | |
| 374 | *lenp = dmareqp->dmar_object.dmao_size; |
| 375 | if (dmareqp->dmar_object.dmao_type == DMA_OTYP_PAGES) { |
| 376 | *mapaddrp = dmareq_pp_mapin(dmareqp->dmar_object.dmao_size, |
| 377 | dmareqp->dmar_object.dmao_obj.pp_obj.pp_offset, |
| 378 | dmareqp->dmar_object.dmao_obj.pp_obj.pp_pp, sleep); |
| 379 | return (*mapaddrp); |
| 380 | } else if (dmareqp->dmar_object.dmao_obj.virt_obj.v_priv != NULL) { |
| 381 | *mapaddrp = dmareq_pplist_mapin(dmareqp->dmar_object.dmao_size, |
| 382 | dmareqp->dmar_object.dmao_obj.virt_obj.v_addr, |
| 383 | dmareqp->dmar_object.dmao_obj.virt_obj.v_priv, |
| 384 | dmareqp->dmar_object.dmao_obj.virt_obj.v_as, sleep); |
| 385 | return (*mapaddrp); |
| 386 | } else if (dmareqp->dmar_object.dmao_obj.virt_obj.v_as == &kas) { |
| 387 | *mapaddrp = NULL; |
| 388 | return (dmareqp->dmar_object.dmao_obj.virt_obj.v_addr); |
| 389 | } else if (dmareqp->dmar_object.dmao_obj.virt_obj.v_as == NULL) { |
| 390 | *mapaddrp = NULL; |
| 391 | return (dmareqp->dmar_object.dmao_obj.virt_obj.v_addr); |
| 392 | } else { |
| 393 | *mapaddrp = dmareq_mapin(dmareqp->dmar_object.dmao_size, |
| 394 | dmareqp->dmar_object.dmao_obj.virt_obj.v_addr, |
| 395 | dmareqp->dmar_object.dmao_obj.virt_obj.v_as, sleep); |
| 396 | return (*mapaddrp); |
| 397 | } |
| 398 | } |
| 399 | |
| 400 | |
| 401 | /* |
| 402 | * support routine - free off kernel virtual mapping as allocated by |
| 403 | * ddi_dmareq_mapin() |
| 404 | */ |
| 405 | static void |
stephh | cecfa35 | 2007-04-05 03:14:36 -0700 | [diff] [blame] | 406 | ddi_dmareq_mapout(caddr_t addr, offset_t len, int map_flags, page_t *pp, |
| 407 | page_t **pplist) |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 408 | { |
| 409 | struct buf buf; |
| 410 | |
| 411 | if (addr == NULL) |
| 412 | return; |
| 413 | /* |
| 414 | * mock up a buf structure |
| 415 | */ |
stephh | cecfa35 | 2007-04-05 03:14:36 -0700 | [diff] [blame] | 416 | buf.b_flags = B_REMAPPED | map_flags; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 417 | buf.b_un.b_addr = addr; |
| 418 | buf.b_bcount = (size_t)len; |
stephh | cecfa35 | 2007-04-05 03:14:36 -0700 | [diff] [blame] | 419 | buf.b_pages = pp; |
| 420 | buf.b_shadow = pplist; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 421 | bp_mapout(&buf); |
| 422 | } |
| 423 | |
| 424 | static time_t |
| 425 | bofi_gettime() |
| 426 | { |
| 427 | timestruc_t ts; |
| 428 | |
| 429 | gethrestime(&ts); |
| 430 | return (ts.tv_sec); |
| 431 | } |
| 432 | |
| 433 | /* |
| 434 | * reset the bus_ops structure of the specified nexus to point to |
| 435 | * the original values in the save_bus_ops structure. |
| 436 | * |
| 437 | * Note that both this routine and modify_bus_ops() rely on the current |
| 438 | * behavior of the framework in that nexus drivers are not unloadable |
| 439 | * |
| 440 | */ |
| 441 | |
| 442 | static int |
| 443 | reset_bus_ops(char *name, struct bus_ops *bop) |
| 444 | { |
| 445 | struct modctl *modp; |
| 446 | struct modldrv *mp; |
| 447 | struct bus_ops *bp; |
| 448 | struct dev_ops *ops; |
| 449 | |
| 450 | mutex_enter(&mod_lock); |
| 451 | /* |
| 452 | * find specified module |
| 453 | */ |
| 454 | modp = &modules; |
| 455 | do { |
| 456 | if (strcmp(name, modp->mod_modname) == 0) { |
| 457 | if (!modp->mod_linkage) { |
| 458 | mutex_exit(&mod_lock); |
| 459 | return (0); |
| 460 | } |
| 461 | mp = modp->mod_linkage->ml_linkage[0]; |
| 462 | if (!mp || !mp->drv_dev_ops) { |
| 463 | mutex_exit(&mod_lock); |
| 464 | return (0); |
| 465 | } |
| 466 | ops = mp->drv_dev_ops; |
| 467 | bp = ops->devo_bus_ops; |
| 468 | if (!bp) { |
| 469 | mutex_exit(&mod_lock); |
| 470 | return (0); |
| 471 | } |
| 472 | if (ops->devo_refcnt > 0) { |
| 473 | /* |
| 474 | * As long as devices are active with modified |
| 475 | * bus ops bofi must not go away. There may be |
| 476 | * drivers with modified access or dma handles. |
| 477 | */ |
| 478 | mutex_exit(&mod_lock); |
| 479 | return (0); |
| 480 | } |
| 481 | cmn_err(CE_NOTE, "bofi reset bus_ops for %s", |
| 482 | mp->drv_linkinfo); |
| 483 | bp->bus_intr_op = bop->bus_intr_op; |
| 484 | bp->bus_post_event = bop->bus_post_event; |
| 485 | bp->bus_map = bop->bus_map; |
| 486 | bp->bus_dma_map = bop->bus_dma_map; |
| 487 | bp->bus_dma_allochdl = bop->bus_dma_allochdl; |
| 488 | bp->bus_dma_freehdl = bop->bus_dma_freehdl; |
| 489 | bp->bus_dma_bindhdl = bop->bus_dma_bindhdl; |
| 490 | bp->bus_dma_unbindhdl = bop->bus_dma_unbindhdl; |
| 491 | bp->bus_dma_flush = bop->bus_dma_flush; |
| 492 | bp->bus_dma_win = bop->bus_dma_win; |
| 493 | bp->bus_dma_ctl = bop->bus_dma_ctl; |
| 494 | mutex_exit(&mod_lock); |
| 495 | return (1); |
| 496 | } |
| 497 | } while ((modp = modp->mod_next) != &modules); |
| 498 | mutex_exit(&mod_lock); |
| 499 | return (0); |
| 500 | } |
| 501 | |
| 502 | /* |
| 503 | * modify the bus_ops structure of the specified nexus to point to bofi |
| 504 | * routines, saving the original values in the save_bus_ops structure |
| 505 | */ |
| 506 | |
| 507 | static int |
| 508 | modify_bus_ops(char *name, struct bus_ops *bop) |
| 509 | { |
| 510 | struct modctl *modp; |
| 511 | struct modldrv *mp; |
| 512 | struct bus_ops *bp; |
| 513 | struct dev_ops *ops; |
| 514 | |
| 515 | if (ddi_name_to_major(name) == -1) |
| 516 | return (0); |
| 517 | |
| 518 | mutex_enter(&mod_lock); |
| 519 | /* |
| 520 | * find specified module |
| 521 | */ |
| 522 | modp = &modules; |
| 523 | do { |
| 524 | if (strcmp(name, modp->mod_modname) == 0) { |
| 525 | if (!modp->mod_linkage) { |
| 526 | mutex_exit(&mod_lock); |
| 527 | return (0); |
| 528 | } |
| 529 | mp = modp->mod_linkage->ml_linkage[0]; |
| 530 | if (!mp || !mp->drv_dev_ops) { |
| 531 | mutex_exit(&mod_lock); |
| 532 | return (0); |
| 533 | } |
| 534 | ops = mp->drv_dev_ops; |
| 535 | bp = ops->devo_bus_ops; |
| 536 | if (!bp) { |
| 537 | mutex_exit(&mod_lock); |
| 538 | return (0); |
| 539 | } |
| 540 | if (ops->devo_refcnt == 0) { |
| 541 | /* |
| 542 | * If there is no device active for this |
| 543 | * module then there is nothing to do for bofi. |
| 544 | */ |
| 545 | mutex_exit(&mod_lock); |
| 546 | return (0); |
| 547 | } |
| 548 | cmn_err(CE_NOTE, "bofi modify bus_ops for %s", |
| 549 | mp->drv_linkinfo); |
| 550 | save_bus_ops = *bp; |
| 551 | bp->bus_intr_op = bop->bus_intr_op; |
| 552 | bp->bus_post_event = bop->bus_post_event; |
| 553 | bp->bus_map = bop->bus_map; |
| 554 | bp->bus_dma_map = bop->bus_dma_map; |
| 555 | bp->bus_dma_allochdl = bop->bus_dma_allochdl; |
| 556 | bp->bus_dma_freehdl = bop->bus_dma_freehdl; |
| 557 | bp->bus_dma_bindhdl = bop->bus_dma_bindhdl; |
| 558 | bp->bus_dma_unbindhdl = bop->bus_dma_unbindhdl; |
| 559 | bp->bus_dma_flush = bop->bus_dma_flush; |
| 560 | bp->bus_dma_win = bop->bus_dma_win; |
| 561 | bp->bus_dma_ctl = bop->bus_dma_ctl; |
| 562 | mutex_exit(&mod_lock); |
| 563 | return (1); |
| 564 | } |
| 565 | } while ((modp = modp->mod_next) != &modules); |
| 566 | mutex_exit(&mod_lock); |
| 567 | return (0); |
| 568 | } |
| 569 | |
| 570 | |
| 571 | int |
| 572 | _init(void) |
| 573 | { |
| 574 | int e; |
| 575 | |
| 576 | e = ddi_soft_state_init(&statep, sizeof (struct bofi_errent), 1); |
| 577 | if (e != 0) |
| 578 | return (e); |
| 579 | if ((e = mod_install(&modlinkage)) != 0) |
| 580 | ddi_soft_state_fini(&statep); |
| 581 | return (e); |
| 582 | } |
| 583 | |
| 584 | |
| 585 | int |
| 586 | _fini(void) |
| 587 | { |
| 588 | int e; |
| 589 | |
| 590 | if ((e = mod_remove(&modlinkage)) != 0) |
| 591 | return (e); |
| 592 | ddi_soft_state_fini(&statep); |
| 593 | return (e); |
| 594 | } |
| 595 | |
| 596 | |
| 597 | int |
| 598 | _info(struct modinfo *modinfop) |
| 599 | { |
| 600 | return (mod_info(&modlinkage, modinfop)); |
| 601 | } |
| 602 | |
| 603 | |
| 604 | static int |
| 605 | bofi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) |
| 606 | { |
| 607 | char *name; |
| 608 | char buf[80]; |
| 609 | int i; |
| 610 | int s, ss; |
| 611 | int size = NAMESIZE; |
| 612 | int new_string; |
| 613 | char *ptr; |
| 614 | |
| 615 | if (cmd != DDI_ATTACH) |
| 616 | return (DDI_FAILURE); |
| 617 | /* |
| 618 | * only one instance - but we clone using the open routine |
| 619 | */ |
| 620 | if (ddi_get_instance(dip) > 0) |
| 621 | return (DDI_FAILURE); |
| 622 | |
| 623 | if (!initialized) { |
| 624 | if ((name = ddi_get_name(dip)) == NULL) |
| 625 | return (DDI_FAILURE); |
| 626 | (void) snprintf(buf, sizeof (buf), "%s,ctl", name); |
| 627 | if (ddi_create_minor_node(dip, buf, S_IFCHR, 0, |
| 628 | DDI_PSEUDO, NULL) == DDI_FAILURE) |
| 629 | return (DDI_FAILURE); |
| 630 | |
| 631 | if (ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_MED, |
| 632 | &bofi_low_cookie) != DDI_SUCCESS) { |
| 633 | ddi_remove_minor_node(dip, buf); |
| 634 | return (DDI_FAILURE); /* fail attach */ |
| 635 | } |
| 636 | /* |
| 637 | * get nexus name (from conf file) |
| 638 | */ |
| 639 | if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF, 0, |
| 640 | "bofi-nexus", nexus_name, &size) != DDI_PROP_SUCCESS) { |
| 641 | ddi_remove_minor_node(dip, buf); |
| 642 | return (DDI_FAILURE); |
| 643 | } |
| 644 | /* |
| 645 | * get whether to do dma map kmem private checking |
| 646 | */ |
| 647 | if ((bofi_range_check = ddi_prop_lookup_string(DDI_DEV_T_ANY, |
| 648 | dip, 0, "bofi-range-check", &ptr)) != DDI_PROP_SUCCESS) |
| 649 | bofi_range_check = 0; |
| 650 | else if (strcmp(ptr, "panic") == 0) |
| 651 | bofi_range_check = 2; |
| 652 | else if (strcmp(ptr, "warn") == 0) |
| 653 | bofi_range_check = 1; |
| 654 | else |
| 655 | bofi_range_check = 0; |
| 656 | ddi_prop_free(ptr); |
| 657 | |
| 658 | /* |
| 659 | * get whether to prevent direct access to register |
| 660 | */ |
| 661 | if ((bofi_ddi_check = ddi_prop_lookup_string(DDI_DEV_T_ANY, |
| 662 | dip, 0, "bofi-ddi-check", &ptr)) != DDI_PROP_SUCCESS) |
| 663 | bofi_ddi_check = 0; |
| 664 | else if (strcmp(ptr, "on") == 0) |
| 665 | bofi_ddi_check = 1; |
| 666 | else |
| 667 | bofi_ddi_check = 0; |
| 668 | ddi_prop_free(ptr); |
| 669 | |
| 670 | /* |
| 671 | * get whether to do copy on ddi_dma_sync |
| 672 | */ |
| 673 | if ((bofi_sync_check = ddi_prop_lookup_string(DDI_DEV_T_ANY, |
| 674 | dip, 0, "bofi-sync-check", &ptr)) != DDI_PROP_SUCCESS) |
| 675 | bofi_sync_check = 0; |
| 676 | else if (strcmp(ptr, "on") == 0) |
| 677 | bofi_sync_check = 1; |
| 678 | else |
| 679 | bofi_sync_check = 0; |
| 680 | ddi_prop_free(ptr); |
| 681 | |
| 682 | /* |
| 683 | * get driver-under-test names (from conf file) |
| 684 | */ |
| 685 | size = NAMESIZE; |
| 686 | if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF, 0, |
| 687 | "bofi-to-test", driver_list, &size) != DDI_PROP_SUCCESS) |
| 688 | driver_list[0] = 0; |
| 689 | /* |
| 690 | * and convert into a sequence of strings |
| 691 | */ |
| 692 | driver_list_neg = 1; |
| 693 | new_string = 1; |
| 694 | driver_list_size = strlen(driver_list); |
| 695 | for (i = 0; i < driver_list_size; i++) { |
| 696 | if (driver_list[i] == ' ') { |
| 697 | driver_list[i] = '\0'; |
| 698 | new_string = 1; |
| 699 | } else if (new_string) { |
| 700 | if (driver_list[i] != '!') |
| 701 | driver_list_neg = 0; |
| 702 | new_string = 0; |
| 703 | } |
| 704 | } |
| 705 | /* |
| 706 | * initialize mutex, lists |
| 707 | */ |
| 708 | mutex_init(&clone_tab_mutex, NULL, MUTEX_DRIVER, |
| 709 | NULL); |
| 710 | /* |
| 711 | * fake up iblock cookie - need to protect outselves |
| 712 | * against drivers that use hilevel interrupts |
| 713 | */ |
| 714 | ss = spl8(); |
| 715 | s = spl8(); |
| 716 | splx(ss); |
| 717 | mutex_init(&bofi_mutex, NULL, MUTEX_SPIN, (void *)(uintptr_t)s); |
| 718 | mutex_init(&bofi_low_mutex, NULL, MUTEX_DRIVER, |
| 719 | (void *)bofi_low_cookie); |
| 720 | shadow_list.next = &shadow_list; |
| 721 | shadow_list.prev = &shadow_list; |
| 722 | for (i = 0; i < HDL_HASH_TBL_SIZE; i++) { |
| 723 | hhash_table[i].hnext = &hhash_table[i]; |
| 724 | hhash_table[i].hprev = &hhash_table[i]; |
| 725 | dhash_table[i].dnext = &dhash_table[i]; |
| 726 | dhash_table[i].dprev = &dhash_table[i]; |
| 727 | } |
| 728 | for (i = 1; i < BOFI_NLINKS; i++) |
| 729 | bofi_link_array[i].link = &bofi_link_array[i-1]; |
| 730 | bofi_link_freelist = &bofi_link_array[BOFI_NLINKS - 1]; |
| 731 | /* |
| 732 | * overlay bus_ops structure |
| 733 | */ |
| 734 | if (modify_bus_ops(nexus_name, &bofi_bus_ops) == 0) { |
| 735 | ddi_remove_minor_node(dip, buf); |
| 736 | mutex_destroy(&clone_tab_mutex); |
| 737 | mutex_destroy(&bofi_mutex); |
| 738 | mutex_destroy(&bofi_low_mutex); |
| 739 | return (DDI_FAILURE); |
| 740 | } |
dilpreet | 00d0963 | 2006-04-23 15:26:28 -0700 | [diff] [blame] | 741 | if (sysevent_evc_bind(FM_ERROR_CHAN, &bofi_error_chan, 0) == 0) |
| 742 | (void) sysevent_evc_subscribe(bofi_error_chan, "bofi", |
| 743 | EC_FM, bofi_fm_ereport_callback, NULL, 0); |
| 744 | |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 745 | /* |
| 746 | * save dip for getinfo |
| 747 | */ |
| 748 | our_dip = dip; |
| 749 | ddi_report_dev(dip); |
| 750 | initialized = 1; |
| 751 | } |
| 752 | return (DDI_SUCCESS); |
| 753 | } |
| 754 | |
| 755 | |
| 756 | static int |
| 757 | bofi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) |
| 758 | { |
| 759 | char *name; |
| 760 | char buf[80]; |
| 761 | |
| 762 | if (cmd != DDI_DETACH) |
| 763 | return (DDI_FAILURE); |
| 764 | if (ddi_get_instance(dip) > 0) |
| 765 | return (DDI_FAILURE); |
| 766 | if ((name = ddi_get_name(dip)) == NULL) |
| 767 | return (DDI_FAILURE); |
| 768 | (void) snprintf(buf, sizeof (buf), "%s,ctl", name); |
| 769 | mutex_enter(&bofi_low_mutex); |
| 770 | mutex_enter(&bofi_mutex); |
| 771 | /* |
| 772 | * make sure test bofi is no longer in use |
| 773 | */ |
| 774 | if (shadow_list.next != &shadow_list || errent_listp != NULL) { |
| 775 | mutex_exit(&bofi_mutex); |
| 776 | mutex_exit(&bofi_low_mutex); |
| 777 | return (DDI_FAILURE); |
| 778 | } |
| 779 | mutex_exit(&bofi_mutex); |
| 780 | mutex_exit(&bofi_low_mutex); |
| 781 | |
| 782 | /* |
| 783 | * restore bus_ops structure |
| 784 | */ |
| 785 | if (reset_bus_ops(nexus_name, &save_bus_ops) == 0) |
| 786 | return (DDI_FAILURE); |
| 787 | |
Gavin Maltby | 49b225e | 2009-11-19 15:28:11 +1100 | [diff] [blame] | 788 | (void) sysevent_evc_unbind(bofi_error_chan); |
dilpreet | 00d0963 | 2006-04-23 15:26:28 -0700 | [diff] [blame] | 789 | |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 790 | mutex_destroy(&clone_tab_mutex); |
| 791 | mutex_destroy(&bofi_mutex); |
| 792 | mutex_destroy(&bofi_low_mutex); |
| 793 | ddi_remove_minor_node(dip, buf); |
| 794 | our_dip = NULL; |
| 795 | initialized = 0; |
| 796 | return (DDI_SUCCESS); |
| 797 | } |
| 798 | |
| 799 | |
| 800 | /* ARGSUSED */ |
| 801 | static int |
| 802 | bofi_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) |
| 803 | { |
| 804 | dev_t dev = (dev_t)arg; |
| 805 | int minor = (int)getminor(dev); |
| 806 | int retval; |
| 807 | |
| 808 | switch (cmd) { |
| 809 | case DDI_INFO_DEVT2DEVINFO: |
| 810 | if (minor != 0 || our_dip == NULL) { |
| 811 | *result = (void *)NULL; |
| 812 | retval = DDI_FAILURE; |
| 813 | } else { |
| 814 | *result = (void *)our_dip; |
| 815 | retval = DDI_SUCCESS; |
| 816 | } |
| 817 | break; |
| 818 | case DDI_INFO_DEVT2INSTANCE: |
| 819 | *result = (void *)0; |
| 820 | retval = DDI_SUCCESS; |
| 821 | break; |
| 822 | default: |
| 823 | retval = DDI_FAILURE; |
| 824 | } |
| 825 | return (retval); |
| 826 | } |
| 827 | |
| 828 | |
| 829 | /* ARGSUSED */ |
| 830 | static int |
| 831 | bofi_open(dev_t *devp, int flag, int otyp, cred_t *credp) |
| 832 | { |
| 833 | int minor = (int)getminor(*devp); |
| 834 | struct bofi_errent *softc; |
| 835 | |
| 836 | /* |
| 837 | * only allow open on minor=0 - the clone device |
| 838 | */ |
| 839 | if (minor != 0) |
| 840 | return (ENXIO); |
| 841 | /* |
| 842 | * fail if not attached |
| 843 | */ |
| 844 | if (!initialized) |
| 845 | return (ENXIO); |
| 846 | /* |
| 847 | * find a free slot and grab it |
| 848 | */ |
| 849 | mutex_enter(&clone_tab_mutex); |
| 850 | for (minor = 1; minor < NCLONES; minor++) { |
| 851 | if (clone_tab[minor] == 0) { |
| 852 | clone_tab[minor] = 1; |
| 853 | break; |
| 854 | } |
| 855 | } |
| 856 | mutex_exit(&clone_tab_mutex); |
| 857 | if (minor == NCLONES) |
| 858 | return (EAGAIN); |
| 859 | /* |
| 860 | * soft state structure for this clone is used to maintain a list |
| 861 | * of allocated errdefs so they can be freed on close |
| 862 | */ |
| 863 | if (ddi_soft_state_zalloc(statep, minor) != DDI_SUCCESS) { |
| 864 | mutex_enter(&clone_tab_mutex); |
| 865 | clone_tab[minor] = 0; |
| 866 | mutex_exit(&clone_tab_mutex); |
| 867 | return (EAGAIN); |
| 868 | } |
| 869 | softc = ddi_get_soft_state(statep, minor); |
| 870 | softc->cnext = softc; |
| 871 | softc->cprev = softc; |
| 872 | |
| 873 | *devp = makedevice(getmajor(*devp), minor); |
| 874 | return (0); |
| 875 | } |
| 876 | |
| 877 | |
| 878 | /* ARGSUSED */ |
| 879 | static int |
| 880 | bofi_close(dev_t dev, int flag, int otyp, cred_t *credp) |
| 881 | { |
| 882 | int minor = (int)getminor(dev); |
| 883 | struct bofi_errent *softc; |
| 884 | struct bofi_errent *ep, *next_ep; |
| 885 | |
| 886 | softc = ddi_get_soft_state(statep, minor); |
| 887 | if (softc == NULL) |
| 888 | return (ENXIO); |
| 889 | /* |
| 890 | * find list of errdefs and free them off |
| 891 | */ |
| 892 | for (ep = softc->cnext; ep != softc; ) { |
| 893 | next_ep = ep->cnext; |
| 894 | (void) bofi_errdef_free(ep); |
| 895 | ep = next_ep; |
| 896 | } |
| 897 | /* |
| 898 | * free clone tab slot |
| 899 | */ |
| 900 | mutex_enter(&clone_tab_mutex); |
| 901 | clone_tab[minor] = 0; |
| 902 | mutex_exit(&clone_tab_mutex); |
| 903 | |
| 904 | ddi_soft_state_free(statep, minor); |
| 905 | return (0); |
| 906 | } |
| 907 | |
| 908 | |
| 909 | /* ARGSUSED */ |
| 910 | static int |
| 911 | bofi_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, |
| 912 | int *rvalp) |
| 913 | { |
| 914 | struct bofi_errent *softc; |
| 915 | int minor = (int)getminor(dev); |
| 916 | struct bofi_errdef errdef; |
| 917 | struct bofi_errctl errctl; |
| 918 | struct bofi_errstate errstate; |
| 919 | void *ed_handle; |
| 920 | struct bofi_get_handles get_handles; |
| 921 | struct bofi_get_hdl_info hdl_info; |
| 922 | struct handle_info *hdlip; |
| 923 | struct handle_info *hib; |
| 924 | |
| 925 | char *buffer; |
| 926 | char *bufptr; |
| 927 | char *endbuf; |
| 928 | int req_count, count, err; |
| 929 | char *namep; |
| 930 | struct bofi_shadow *hp; |
| 931 | int retval; |
| 932 | struct bofi_shadow *hhashp; |
| 933 | int i; |
| 934 | |
| 935 | switch (cmd) { |
| 936 | case BOFI_ADD_DEF: |
| 937 | /* |
| 938 | * add a new error definition |
| 939 | */ |
| 940 | #ifdef _MULTI_DATAMODEL |
| 941 | switch (ddi_model_convert_from(mode & FMODELS)) { |
| 942 | case DDI_MODEL_ILP32: |
| 943 | { |
| 944 | /* |
| 945 | * For use when a 32 bit app makes a call into a |
| 946 | * 64 bit ioctl |
| 947 | */ |
| 948 | struct bofi_errdef32 errdef_32; |
| 949 | |
| 950 | if (ddi_copyin((void *)arg, &errdef_32, |
| 951 | sizeof (struct bofi_errdef32), mode)) { |
| 952 | return (EFAULT); |
| 953 | } |
| 954 | errdef.namesize = errdef_32.namesize; |
| 955 | (void) strncpy(errdef.name, errdef_32.name, NAMESIZE); |
| 956 | errdef.instance = errdef_32.instance; |
| 957 | errdef.rnumber = errdef_32.rnumber; |
| 958 | errdef.offset = errdef_32.offset; |
| 959 | errdef.len = errdef_32.len; |
| 960 | errdef.access_type = errdef_32.access_type; |
| 961 | errdef.access_count = errdef_32.access_count; |
| 962 | errdef.fail_count = errdef_32.fail_count; |
| 963 | errdef.acc_chk = errdef_32.acc_chk; |
| 964 | errdef.optype = errdef_32.optype; |
| 965 | errdef.operand = errdef_32.operand; |
| 966 | errdef.log.logsize = errdef_32.log.logsize; |
| 967 | errdef.log.entries = errdef_32.log.entries; |
| 968 | errdef.log.flags = errdef_32.log.flags; |
| 969 | errdef.log.wrapcnt = errdef_32.log.wrapcnt; |
| 970 | errdef.log.start_time = errdef_32.log.start_time; |
| 971 | errdef.log.stop_time = errdef_32.log.stop_time; |
| 972 | errdef.log.logbase = |
| 973 | (caddr_t)(uintptr_t)errdef_32.log.logbase; |
| 974 | errdef.errdef_handle = errdef_32.errdef_handle; |
| 975 | break; |
| 976 | } |
| 977 | case DDI_MODEL_NONE: |
| 978 | if (ddi_copyin((void *)arg, &errdef, |
| 979 | sizeof (struct bofi_errdef), mode)) |
| 980 | return (EFAULT); |
| 981 | break; |
| 982 | } |
| 983 | #else /* ! _MULTI_DATAMODEL */ |
| 984 | if (ddi_copyin((void *)arg, &errdef, |
| 985 | sizeof (struct bofi_errdef), mode) != 0) |
| 986 | return (EFAULT); |
| 987 | #endif /* _MULTI_DATAMODEL */ |
| 988 | /* |
| 989 | * do some validation |
| 990 | */ |
| 991 | if (errdef.fail_count == 0) |
| 992 | errdef.optype = 0; |
| 993 | if (errdef.optype != 0) { |
| 994 | if (errdef.access_type & BOFI_INTR && |
| 995 | errdef.optype != BOFI_DELAY_INTR && |
| 996 | errdef.optype != BOFI_LOSE_INTR && |
| 997 | errdef.optype != BOFI_EXTRA_INTR) |
| 998 | return (EINVAL); |
| 999 | if ((errdef.access_type & (BOFI_DMA_RW|BOFI_PIO_R)) && |
| 1000 | errdef.optype == BOFI_NO_TRANSFER) |
| 1001 | return (EINVAL); |
| 1002 | if ((errdef.access_type & (BOFI_PIO_RW)) && |
| 1003 | errdef.optype != BOFI_EQUAL && |
| 1004 | errdef.optype != BOFI_OR && |
| 1005 | errdef.optype != BOFI_XOR && |
| 1006 | errdef.optype != BOFI_AND && |
| 1007 | errdef.optype != BOFI_NO_TRANSFER) |
| 1008 | return (EINVAL); |
| 1009 | } |
| 1010 | /* |
| 1011 | * find softstate for this clone, so we can tag |
| 1012 | * new errdef on to it |
| 1013 | */ |
| 1014 | softc = ddi_get_soft_state(statep, minor); |
| 1015 | if (softc == NULL) |
| 1016 | return (ENXIO); |
| 1017 | /* |
| 1018 | * read in name |
| 1019 | */ |
| 1020 | if (errdef.namesize > NAMESIZE) |
| 1021 | return (EINVAL); |
| 1022 | namep = kmem_zalloc(errdef.namesize+1, KM_SLEEP); |
| 1023 | (void) strncpy(namep, errdef.name, errdef.namesize); |
| 1024 | |
| 1025 | if (bofi_errdef_alloc(&errdef, namep, softc) != DDI_SUCCESS) { |
| 1026 | (void) bofi_errdef_free((struct bofi_errent *) |
| 1027 | (uintptr_t)errdef.errdef_handle); |
| 1028 | kmem_free(namep, errdef.namesize+1); |
| 1029 | return (EINVAL); |
| 1030 | } |
| 1031 | /* |
| 1032 | * copy out errdef again, including filled in errdef_handle |
| 1033 | */ |
| 1034 | #ifdef _MULTI_DATAMODEL |
| 1035 | switch (ddi_model_convert_from(mode & FMODELS)) { |
| 1036 | case DDI_MODEL_ILP32: |
| 1037 | { |
| 1038 | /* |
| 1039 | * For use when a 32 bit app makes a call into a |
| 1040 | * 64 bit ioctl |
| 1041 | */ |
| 1042 | struct bofi_errdef32 errdef_32; |
| 1043 | |
| 1044 | errdef_32.namesize = errdef.namesize; |
| 1045 | (void) strncpy(errdef_32.name, errdef.name, NAMESIZE); |
| 1046 | errdef_32.instance = errdef.instance; |
| 1047 | errdef_32.rnumber = errdef.rnumber; |
| 1048 | errdef_32.offset = errdef.offset; |
| 1049 | errdef_32.len = errdef.len; |
| 1050 | errdef_32.access_type = errdef.access_type; |
| 1051 | errdef_32.access_count = errdef.access_count; |
| 1052 | errdef_32.fail_count = errdef.fail_count; |
| 1053 | errdef_32.acc_chk = errdef.acc_chk; |
| 1054 | errdef_32.optype = errdef.optype; |
| 1055 | errdef_32.operand = errdef.operand; |
| 1056 | errdef_32.log.logsize = errdef.log.logsize; |
| 1057 | errdef_32.log.entries = errdef.log.entries; |
| 1058 | errdef_32.log.flags = errdef.log.flags; |
| 1059 | errdef_32.log.wrapcnt = errdef.log.wrapcnt; |
| 1060 | errdef_32.log.start_time = errdef.log.start_time; |
| 1061 | errdef_32.log.stop_time = errdef.log.stop_time; |
| 1062 | errdef_32.log.logbase = |
| 1063 | (caddr32_t)(uintptr_t)errdef.log.logbase; |
| 1064 | errdef_32.errdef_handle = errdef.errdef_handle; |
| 1065 | if (ddi_copyout(&errdef_32, (void *)arg, |
| 1066 | sizeof (struct bofi_errdef32), mode) != 0) { |
| 1067 | (void) bofi_errdef_free((struct bofi_errent *) |
| 1068 | errdef.errdef_handle); |
| 1069 | kmem_free(namep, errdef.namesize+1); |
| 1070 | return (EFAULT); |
| 1071 | } |
| 1072 | break; |
| 1073 | } |
| 1074 | case DDI_MODEL_NONE: |
| 1075 | if (ddi_copyout(&errdef, (void *)arg, |
| 1076 | sizeof (struct bofi_errdef), mode) != 0) { |
| 1077 | (void) bofi_errdef_free((struct bofi_errent *) |
| 1078 | errdef.errdef_handle); |
| 1079 | kmem_free(namep, errdef.namesize+1); |
| 1080 | return (EFAULT); |
| 1081 | } |
| 1082 | break; |
| 1083 | } |
| 1084 | #else /* ! _MULTI_DATAMODEL */ |
| 1085 | if (ddi_copyout(&errdef, (void *)arg, |
| 1086 | sizeof (struct bofi_errdef), mode) != 0) { |
| 1087 | (void) bofi_errdef_free((struct bofi_errent *) |
| 1088 | (uintptr_t)errdef.errdef_handle); |
| 1089 | kmem_free(namep, errdef.namesize+1); |
| 1090 | return (EFAULT); |
| 1091 | } |
| 1092 | #endif /* _MULTI_DATAMODEL */ |
| 1093 | return (0); |
| 1094 | case BOFI_DEL_DEF: |
| 1095 | /* |
| 1096 | * delete existing errdef |
| 1097 | */ |
| 1098 | if (ddi_copyin((void *)arg, &ed_handle, |
| 1099 | sizeof (void *), mode) != 0) |
| 1100 | return (EFAULT); |
| 1101 | return (bofi_errdef_free((struct bofi_errent *)ed_handle)); |
| 1102 | case BOFI_START: |
| 1103 | /* |
| 1104 | * start all errdefs corresponding to |
| 1105 | * this name and instance |
| 1106 | */ |
| 1107 | if (ddi_copyin((void *)arg, &errctl, |
| 1108 | sizeof (struct bofi_errctl), mode) != 0) |
| 1109 | return (EFAULT); |
| 1110 | /* |
| 1111 | * copy in name |
| 1112 | */ |
| 1113 | if (errctl.namesize > NAMESIZE) |
| 1114 | return (EINVAL); |
| 1115 | namep = kmem_zalloc(errctl.namesize+1, KM_SLEEP); |
| 1116 | (void) strncpy(namep, errctl.name, errctl.namesize); |
| 1117 | bofi_start(&errctl, namep); |
| 1118 | kmem_free(namep, errctl.namesize+1); |
| 1119 | return (0); |
| 1120 | case BOFI_STOP: |
| 1121 | /* |
| 1122 | * stop all errdefs corresponding to |
| 1123 | * this name and instance |
| 1124 | */ |
| 1125 | if (ddi_copyin((void *)arg, &errctl, |
| 1126 | sizeof (struct bofi_errctl), mode) != 0) |
| 1127 | return (EFAULT); |
| 1128 | /* |
| 1129 | * copy in name |
| 1130 | */ |
| 1131 | if (errctl.namesize > NAMESIZE) |
| 1132 | return (EINVAL); |
| 1133 | namep = kmem_zalloc(errctl.namesize+1, KM_SLEEP); |
| 1134 | (void) strncpy(namep, errctl.name, errctl.namesize); |
| 1135 | bofi_stop(&errctl, namep); |
| 1136 | kmem_free(namep, errctl.namesize+1); |
| 1137 | return (0); |
| 1138 | case BOFI_BROADCAST: |
| 1139 | /* |
| 1140 | * wakeup all errdefs corresponding to |
| 1141 | * this name and instance |
| 1142 | */ |
| 1143 | if (ddi_copyin((void *)arg, &errctl, |
| 1144 | sizeof (struct bofi_errctl), mode) != 0) |
| 1145 | return (EFAULT); |
| 1146 | /* |
| 1147 | * copy in name |
| 1148 | */ |
| 1149 | if (errctl.namesize > NAMESIZE) |
| 1150 | return (EINVAL); |
| 1151 | namep = kmem_zalloc(errctl.namesize+1, KM_SLEEP); |
| 1152 | (void) strncpy(namep, errctl.name, errctl.namesize); |
| 1153 | bofi_broadcast(&errctl, namep); |
| 1154 | kmem_free(namep, errctl.namesize+1); |
| 1155 | return (0); |
| 1156 | case BOFI_CLEAR_ACC_CHK: |
| 1157 | /* |
| 1158 | * clear "acc_chk" for all errdefs corresponding to |
| 1159 | * this name and instance |
| 1160 | */ |
| 1161 | if (ddi_copyin((void *)arg, &errctl, |
| 1162 | sizeof (struct bofi_errctl), mode) != 0) |
| 1163 | return (EFAULT); |
| 1164 | /* |
| 1165 | * copy in name |
| 1166 | */ |
| 1167 | if (errctl.namesize > NAMESIZE) |
| 1168 | return (EINVAL); |
| 1169 | namep = kmem_zalloc(errctl.namesize+1, KM_SLEEP); |
| 1170 | (void) strncpy(namep, errctl.name, errctl.namesize); |
| 1171 | bofi_clear_acc_chk(&errctl, namep); |
| 1172 | kmem_free(namep, errctl.namesize+1); |
| 1173 | return (0); |
| 1174 | case BOFI_CLEAR_ERRORS: |
| 1175 | /* |
| 1176 | * set "fail_count" to 0 for all errdefs corresponding to |
| 1177 | * this name and instance whose "access_count" |
| 1178 | * has expired. |
| 1179 | */ |
| 1180 | if (ddi_copyin((void *)arg, &errctl, |
| 1181 | sizeof (struct bofi_errctl), mode) != 0) |
| 1182 | return (EFAULT); |
| 1183 | /* |
| 1184 | * copy in name |
| 1185 | */ |
| 1186 | if (errctl.namesize > NAMESIZE) |
| 1187 | return (EINVAL); |
| 1188 | namep = kmem_zalloc(errctl.namesize+1, KM_SLEEP); |
| 1189 | (void) strncpy(namep, errctl.name, errctl.namesize); |
| 1190 | bofi_clear_errors(&errctl, namep); |
| 1191 | kmem_free(namep, errctl.namesize+1); |
| 1192 | return (0); |
| 1193 | case BOFI_CLEAR_ERRDEFS: |
| 1194 | /* |
| 1195 | * set "access_count" and "fail_count" to 0 for all errdefs |
| 1196 | * corresponding to this name and instance |
| 1197 | */ |
| 1198 | if (ddi_copyin((void *)arg, &errctl, |
| 1199 | sizeof (struct bofi_errctl), mode) != 0) |
| 1200 | return (EFAULT); |
| 1201 | /* |
| 1202 | * copy in name |
| 1203 | */ |
| 1204 | if (errctl.namesize > NAMESIZE) |
| 1205 | return (EINVAL); |
| 1206 | namep = kmem_zalloc(errctl.namesize+1, KM_SLEEP); |
| 1207 | (void) strncpy(namep, errctl.name, errctl.namesize); |
| 1208 | bofi_clear_errdefs(&errctl, namep); |
| 1209 | kmem_free(namep, errctl.namesize+1); |
| 1210 | return (0); |
| 1211 | case BOFI_CHK_STATE: |
| 1212 | { |
| 1213 | struct acc_log_elem *klg; |
| 1214 | size_t uls; |
| 1215 | /* |
| 1216 | * get state for this errdef - read in dummy errstate |
| 1217 | * with just the errdef_handle filled in |
| 1218 | */ |
| 1219 | #ifdef _MULTI_DATAMODEL |
| 1220 | switch (ddi_model_convert_from(mode & FMODELS)) { |
| 1221 | case DDI_MODEL_ILP32: |
| 1222 | { |
| 1223 | /* |
| 1224 | * For use when a 32 bit app makes a call into a |
| 1225 | * 64 bit ioctl |
| 1226 | */ |
| 1227 | struct bofi_errstate32 errstate_32; |
| 1228 | |
| 1229 | if (ddi_copyin((void *)arg, &errstate_32, |
| 1230 | sizeof (struct bofi_errstate32), mode) != 0) { |
| 1231 | return (EFAULT); |
| 1232 | } |
| 1233 | errstate.fail_time = errstate_32.fail_time; |
| 1234 | errstate.msg_time = errstate_32.msg_time; |
| 1235 | errstate.access_count = errstate_32.access_count; |
| 1236 | errstate.fail_count = errstate_32.fail_count; |
| 1237 | errstate.acc_chk = errstate_32.acc_chk; |
| 1238 | errstate.errmsg_count = errstate_32.errmsg_count; |
| 1239 | (void) strncpy(errstate.buffer, errstate_32.buffer, |
| 1240 | ERRMSGSIZE); |
| 1241 | errstate.severity = errstate_32.severity; |
| 1242 | errstate.log.logsize = errstate_32.log.logsize; |
| 1243 | errstate.log.entries = errstate_32.log.entries; |
| 1244 | errstate.log.flags = errstate_32.log.flags; |
| 1245 | errstate.log.wrapcnt = errstate_32.log.wrapcnt; |
| 1246 | errstate.log.start_time = errstate_32.log.start_time; |
| 1247 | errstate.log.stop_time = errstate_32.log.stop_time; |
| 1248 | errstate.log.logbase = |
| 1249 | (caddr_t)(uintptr_t)errstate_32.log.logbase; |
| 1250 | errstate.errdef_handle = errstate_32.errdef_handle; |
| 1251 | break; |
| 1252 | } |
| 1253 | case DDI_MODEL_NONE: |
| 1254 | if (ddi_copyin((void *)arg, &errstate, |
| 1255 | sizeof (struct bofi_errstate), mode) != 0) |
| 1256 | return (EFAULT); |
| 1257 | break; |
| 1258 | } |
| 1259 | #else /* ! _MULTI_DATAMODEL */ |
| 1260 | if (ddi_copyin((void *)arg, &errstate, |
| 1261 | sizeof (struct bofi_errstate), mode) != 0) |
| 1262 | return (EFAULT); |
| 1263 | #endif /* _MULTI_DATAMODEL */ |
| 1264 | if ((retval = bofi_errdef_check(&errstate, &klg)) == EINVAL) |
| 1265 | return (EINVAL); |
| 1266 | /* |
| 1267 | * copy out real errstate structure |
| 1268 | */ |
| 1269 | uls = errstate.log.logsize; |
| 1270 | if (errstate.log.entries > uls && uls) |
| 1271 | /* insufficient user memory */ |
| 1272 | errstate.log.entries = uls; |
| 1273 | /* always pass back a time */ |
| 1274 | if (errstate.log.stop_time == 0ul) |
| 1275 | (void) drv_getparm(TIME, &(errstate.log.stop_time)); |
| 1276 | |
| 1277 | #ifdef _MULTI_DATAMODEL |
| 1278 | switch (ddi_model_convert_from(mode & FMODELS)) { |
| 1279 | case DDI_MODEL_ILP32: |
| 1280 | { |
| 1281 | /* |
| 1282 | * For use when a 32 bit app makes a call into a |
| 1283 | * 64 bit ioctl |
| 1284 | */ |
| 1285 | struct bofi_errstate32 errstate_32; |
| 1286 | |
| 1287 | errstate_32.fail_time = errstate.fail_time; |
| 1288 | errstate_32.msg_time = errstate.msg_time; |
| 1289 | errstate_32.access_count = errstate.access_count; |
| 1290 | errstate_32.fail_count = errstate.fail_count; |
| 1291 | errstate_32.acc_chk = errstate.acc_chk; |
| 1292 | errstate_32.errmsg_count = errstate.errmsg_count; |
| 1293 | (void) strncpy(errstate_32.buffer, errstate.buffer, |
| 1294 | ERRMSGSIZE); |
| 1295 | errstate_32.severity = errstate.severity; |
| 1296 | errstate_32.log.logsize = errstate.log.logsize; |
| 1297 | errstate_32.log.entries = errstate.log.entries; |
| 1298 | errstate_32.log.flags = errstate.log.flags; |
| 1299 | errstate_32.log.wrapcnt = errstate.log.wrapcnt; |
| 1300 | errstate_32.log.start_time = errstate.log.start_time; |
| 1301 | errstate_32.log.stop_time = errstate.log.stop_time; |
| 1302 | errstate_32.log.logbase = |
| 1303 | (caddr32_t)(uintptr_t)errstate.log.logbase; |
| 1304 | errstate_32.errdef_handle = errstate.errdef_handle; |
| 1305 | if (ddi_copyout(&errstate_32, (void *)arg, |
| 1306 | sizeof (struct bofi_errstate32), mode) != 0) |
| 1307 | return (EFAULT); |
| 1308 | break; |
| 1309 | } |
| 1310 | case DDI_MODEL_NONE: |
| 1311 | if (ddi_copyout(&errstate, (void *)arg, |
| 1312 | sizeof (struct bofi_errstate), mode) != 0) |
| 1313 | return (EFAULT); |
| 1314 | break; |
| 1315 | } |
| 1316 | #else /* ! _MULTI_DATAMODEL */ |
| 1317 | if (ddi_copyout(&errstate, (void *)arg, |
| 1318 | sizeof (struct bofi_errstate), mode) != 0) |
| 1319 | return (EFAULT); |
| 1320 | #endif /* _MULTI_DATAMODEL */ |
| 1321 | if (uls && errstate.log.entries && |
| 1322 | ddi_copyout(klg, errstate.log.logbase, |
| 1323 | errstate.log.entries * sizeof (struct acc_log_elem), |
| 1324 | mode) != 0) { |
| 1325 | return (EFAULT); |
| 1326 | } |
| 1327 | return (retval); |
| 1328 | } |
| 1329 | case BOFI_CHK_STATE_W: |
| 1330 | { |
| 1331 | struct acc_log_elem *klg; |
| 1332 | size_t uls; |
| 1333 | /* |
| 1334 | * get state for this errdef - read in dummy errstate |
| 1335 | * with just the errdef_handle filled in. Then wait for |
| 1336 | * a ddi_report_fault message to come back |
| 1337 | */ |
| 1338 | #ifdef _MULTI_DATAMODEL |
| 1339 | switch (ddi_model_convert_from(mode & FMODELS)) { |
| 1340 | case DDI_MODEL_ILP32: |
| 1341 | { |
| 1342 | /* |
| 1343 | * For use when a 32 bit app makes a call into a |
| 1344 | * 64 bit ioctl |
| 1345 | */ |
| 1346 | struct bofi_errstate32 errstate_32; |
| 1347 | |
| 1348 | if (ddi_copyin((void *)arg, &errstate_32, |
| 1349 | sizeof (struct bofi_errstate32), mode) != 0) { |
| 1350 | return (EFAULT); |
| 1351 | } |
| 1352 | errstate.fail_time = errstate_32.fail_time; |
| 1353 | errstate.msg_time = errstate_32.msg_time; |
| 1354 | errstate.access_count = errstate_32.access_count; |
| 1355 | errstate.fail_count = errstate_32.fail_count; |
| 1356 | errstate.acc_chk = errstate_32.acc_chk; |
| 1357 | errstate.errmsg_count = errstate_32.errmsg_count; |
| 1358 | (void) strncpy(errstate.buffer, errstate_32.buffer, |
| 1359 | ERRMSGSIZE); |
| 1360 | errstate.severity = errstate_32.severity; |
| 1361 | errstate.log.logsize = errstate_32.log.logsize; |
| 1362 | errstate.log.entries = errstate_32.log.entries; |
| 1363 | errstate.log.flags = errstate_32.log.flags; |
| 1364 | errstate.log.wrapcnt = errstate_32.log.wrapcnt; |
| 1365 | errstate.log.start_time = errstate_32.log.start_time; |
| 1366 | errstate.log.stop_time = errstate_32.log.stop_time; |
| 1367 | errstate.log.logbase = |
| 1368 | (caddr_t)(uintptr_t)errstate_32.log.logbase; |
| 1369 | errstate.errdef_handle = errstate_32.errdef_handle; |
| 1370 | break; |
| 1371 | } |
| 1372 | case DDI_MODEL_NONE: |
| 1373 | if (ddi_copyin((void *)arg, &errstate, |
| 1374 | sizeof (struct bofi_errstate), mode) != 0) |
| 1375 | return (EFAULT); |
| 1376 | break; |
| 1377 | } |
| 1378 | #else /* ! _MULTI_DATAMODEL */ |
| 1379 | if (ddi_copyin((void *)arg, &errstate, |
| 1380 | sizeof (struct bofi_errstate), mode) != 0) |
| 1381 | return (EFAULT); |
| 1382 | #endif /* _MULTI_DATAMODEL */ |
| 1383 | if ((retval = bofi_errdef_check_w(&errstate, &klg)) == EINVAL) |
| 1384 | return (EINVAL); |
| 1385 | /* |
| 1386 | * copy out real errstate structure |
| 1387 | */ |
| 1388 | uls = errstate.log.logsize; |
| 1389 | uls = errstate.log.logsize; |
| 1390 | if (errstate.log.entries > uls && uls) |
| 1391 | /* insufficient user memory */ |
| 1392 | errstate.log.entries = uls; |
| 1393 | /* always pass back a time */ |
| 1394 | if (errstate.log.stop_time == 0ul) |
| 1395 | (void) drv_getparm(TIME, &(errstate.log.stop_time)); |
| 1396 | |
| 1397 | #ifdef _MULTI_DATAMODEL |
| 1398 | switch (ddi_model_convert_from(mode & FMODELS)) { |
| 1399 | case DDI_MODEL_ILP32: |
| 1400 | { |
| 1401 | /* |
| 1402 | * For use when a 32 bit app makes a call into a |
| 1403 | * 64 bit ioctl |
| 1404 | */ |
| 1405 | struct bofi_errstate32 errstate_32; |
| 1406 | |
| 1407 | errstate_32.fail_time = errstate.fail_time; |
| 1408 | errstate_32.msg_time = errstate.msg_time; |
| 1409 | errstate_32.access_count = errstate.access_count; |
| 1410 | errstate_32.fail_count = errstate.fail_count; |
| 1411 | errstate_32.acc_chk = errstate.acc_chk; |
| 1412 | errstate_32.errmsg_count = errstate.errmsg_count; |
| 1413 | (void) strncpy(errstate_32.buffer, errstate.buffer, |
| 1414 | ERRMSGSIZE); |
| 1415 | errstate_32.severity = errstate.severity; |
| 1416 | errstate_32.log.logsize = errstate.log.logsize; |
| 1417 | errstate_32.log.entries = errstate.log.entries; |
| 1418 | errstate_32.log.flags = errstate.log.flags; |
| 1419 | errstate_32.log.wrapcnt = errstate.log.wrapcnt; |
| 1420 | errstate_32.log.start_time = errstate.log.start_time; |
| 1421 | errstate_32.log.stop_time = errstate.log.stop_time; |
| 1422 | errstate_32.log.logbase = |
| 1423 | (caddr32_t)(uintptr_t)errstate.log.logbase; |
| 1424 | errstate_32.errdef_handle = errstate.errdef_handle; |
| 1425 | if (ddi_copyout(&errstate_32, (void *)arg, |
| 1426 | sizeof (struct bofi_errstate32), mode) != 0) |
| 1427 | return (EFAULT); |
| 1428 | break; |
| 1429 | } |
| 1430 | case DDI_MODEL_NONE: |
| 1431 | if (ddi_copyout(&errstate, (void *)arg, |
| 1432 | sizeof (struct bofi_errstate), mode) != 0) |
| 1433 | return (EFAULT); |
| 1434 | break; |
| 1435 | } |
| 1436 | #else /* ! _MULTI_DATAMODEL */ |
| 1437 | if (ddi_copyout(&errstate, (void *)arg, |
| 1438 | sizeof (struct bofi_errstate), mode) != 0) |
| 1439 | return (EFAULT); |
| 1440 | #endif /* _MULTI_DATAMODEL */ |
| 1441 | |
| 1442 | if (uls && errstate.log.entries && |
| 1443 | ddi_copyout(klg, errstate.log.logbase, |
| 1444 | errstate.log.entries * sizeof (struct acc_log_elem), |
| 1445 | mode) != 0) { |
| 1446 | return (EFAULT); |
| 1447 | } |
| 1448 | return (retval); |
| 1449 | } |
| 1450 | case BOFI_GET_HANDLES: |
| 1451 | /* |
| 1452 | * display existing handles |
| 1453 | */ |
| 1454 | #ifdef _MULTI_DATAMODEL |
| 1455 | switch (ddi_model_convert_from(mode & FMODELS)) { |
| 1456 | case DDI_MODEL_ILP32: |
| 1457 | { |
| 1458 | /* |
| 1459 | * For use when a 32 bit app makes a call into a |
| 1460 | * 64 bit ioctl |
| 1461 | */ |
| 1462 | struct bofi_get_handles32 get_handles_32; |
| 1463 | |
| 1464 | if (ddi_copyin((void *)arg, &get_handles_32, |
| 1465 | sizeof (get_handles_32), mode) != 0) { |
| 1466 | return (EFAULT); |
| 1467 | } |
| 1468 | get_handles.namesize = get_handles_32.namesize; |
| 1469 | (void) strncpy(get_handles.name, get_handles_32.name, |
| 1470 | NAMESIZE); |
| 1471 | get_handles.instance = get_handles_32.instance; |
| 1472 | get_handles.count = get_handles_32.count; |
| 1473 | get_handles.buffer = |
| 1474 | (caddr_t)(uintptr_t)get_handles_32.buffer; |
| 1475 | break; |
| 1476 | } |
| 1477 | case DDI_MODEL_NONE: |
| 1478 | if (ddi_copyin((void *)arg, &get_handles, |
| 1479 | sizeof (get_handles), mode) != 0) |
| 1480 | return (EFAULT); |
| 1481 | break; |
| 1482 | } |
| 1483 | #else /* ! _MULTI_DATAMODEL */ |
| 1484 | if (ddi_copyin((void *)arg, &get_handles, |
| 1485 | sizeof (get_handles), mode) != 0) |
| 1486 | return (EFAULT); |
| 1487 | #endif /* _MULTI_DATAMODEL */ |
| 1488 | /* |
| 1489 | * read in name |
| 1490 | */ |
| 1491 | if (get_handles.namesize > NAMESIZE) |
| 1492 | return (EINVAL); |
| 1493 | namep = kmem_zalloc(get_handles.namesize+1, KM_SLEEP); |
| 1494 | (void) strncpy(namep, get_handles.name, get_handles.namesize); |
| 1495 | req_count = get_handles.count; |
| 1496 | bufptr = buffer = kmem_zalloc(req_count, KM_SLEEP); |
| 1497 | endbuf = bufptr + req_count; |
| 1498 | /* |
| 1499 | * display existing handles |
| 1500 | */ |
| 1501 | mutex_enter(&bofi_low_mutex); |
| 1502 | mutex_enter(&bofi_mutex); |
| 1503 | for (i = 0; i < HDL_HASH_TBL_SIZE; i++) { |
| 1504 | hhashp = &hhash_table[i]; |
| 1505 | for (hp = hhashp->hnext; hp != hhashp; hp = hp->hnext) { |
| 1506 | if (!driver_under_test(hp->dip)) |
| 1507 | continue; |
| 1508 | if (ddi_name_to_major(ddi_get_name(hp->dip)) != |
| 1509 | ddi_name_to_major(namep)) |
| 1510 | continue; |
| 1511 | if (hp->instance != get_handles.instance) |
| 1512 | continue; |
| 1513 | /* |
| 1514 | * print information per handle - note that |
| 1515 | * DMA* means an unbound DMA handle |
| 1516 | */ |
| 1517 | (void) snprintf(bufptr, (size_t)(endbuf-bufptr), |
| 1518 | " %s %d %s ", hp->name, hp->instance, |
| 1519 | (hp->type == BOFI_INT_HDL) ? "INTR" : |
| 1520 | (hp->type == BOFI_ACC_HDL) ? "PIO" : |
| 1521 | (hp->type == BOFI_DMA_HDL) ? "DMA" : |
| 1522 | (hp->hparrayp != NULL) ? "DVMA" : "DMA*"); |
| 1523 | bufptr += strlen(bufptr); |
| 1524 | if (hp->type == BOFI_ACC_HDL) { |
| 1525 | if (hp->len == INT_MAX - hp->offset) |
| 1526 | (void) snprintf(bufptr, |
| 1527 | (size_t)(endbuf-bufptr), |
| 1528 | "reg set %d off 0x%llx\n", |
| 1529 | hp->rnumber, hp->offset); |
| 1530 | else |
| 1531 | (void) snprintf(bufptr, |
| 1532 | (size_t)(endbuf-bufptr), |
| 1533 | "reg set %d off 0x%llx" |
| 1534 | " len 0x%llx\n", |
| 1535 | hp->rnumber, hp->offset, |
| 1536 | hp->len); |
| 1537 | } else if (hp->type == BOFI_DMA_HDL) |
| 1538 | (void) snprintf(bufptr, |
| 1539 | (size_t)(endbuf-bufptr), |
| 1540 | "handle no %d len 0x%llx" |
| 1541 | " addr 0x%p\n", hp->rnumber, |
| 1542 | hp->len, (void *)hp->addr); |
| 1543 | else if (hp->type == BOFI_NULL && |
| 1544 | hp->hparrayp == NULL) |
| 1545 | (void) snprintf(bufptr, |
| 1546 | (size_t)(endbuf-bufptr), |
| 1547 | "handle no %d\n", hp->rnumber); |
| 1548 | else |
| 1549 | (void) snprintf(bufptr, |
| 1550 | (size_t)(endbuf-bufptr), "\n"); |
| 1551 | bufptr += strlen(bufptr); |
| 1552 | } |
| 1553 | } |
| 1554 | mutex_exit(&bofi_mutex); |
| 1555 | mutex_exit(&bofi_low_mutex); |
| 1556 | err = ddi_copyout(buffer, get_handles.buffer, req_count, mode); |
| 1557 | kmem_free(namep, get_handles.namesize+1); |
| 1558 | kmem_free(buffer, req_count); |
| 1559 | if (err != 0) |
| 1560 | return (EFAULT); |
| 1561 | else |
| 1562 | return (0); |
| 1563 | case BOFI_GET_HANDLE_INFO: |
| 1564 | /* |
| 1565 | * display existing handles |
| 1566 | */ |
| 1567 | #ifdef _MULTI_DATAMODEL |
| 1568 | switch (ddi_model_convert_from(mode & FMODELS)) { |
| 1569 | case DDI_MODEL_ILP32: |
| 1570 | { |
| 1571 | /* |
| 1572 | * For use when a 32 bit app makes a call into a |
| 1573 | * 64 bit ioctl |
| 1574 | */ |
| 1575 | struct bofi_get_hdl_info32 hdl_info_32; |
| 1576 | |
| 1577 | if (ddi_copyin((void *)arg, &hdl_info_32, |
| 1578 | sizeof (hdl_info_32), mode)) { |
| 1579 | return (EFAULT); |
| 1580 | } |
| 1581 | hdl_info.namesize = hdl_info_32.namesize; |
| 1582 | (void) strncpy(hdl_info.name, hdl_info_32.name, |
| 1583 | NAMESIZE); |
| 1584 | hdl_info.count = hdl_info_32.count; |
| 1585 | hdl_info.hdli = (caddr_t)(uintptr_t)hdl_info_32.hdli; |
| 1586 | break; |
| 1587 | } |
| 1588 | case DDI_MODEL_NONE: |
| 1589 | if (ddi_copyin((void *)arg, &hdl_info, |
| 1590 | sizeof (hdl_info), mode)) |
| 1591 | return (EFAULT); |
| 1592 | break; |
| 1593 | } |
| 1594 | #else /* ! _MULTI_DATAMODEL */ |
| 1595 | if (ddi_copyin((void *)arg, &hdl_info, |
| 1596 | sizeof (hdl_info), mode)) |
| 1597 | return (EFAULT); |
| 1598 | #endif /* _MULTI_DATAMODEL */ |
| 1599 | if (hdl_info.namesize > NAMESIZE) |
| 1600 | return (EINVAL); |
| 1601 | namep = kmem_zalloc(hdl_info.namesize + 1, KM_SLEEP); |
| 1602 | (void) strncpy(namep, hdl_info.name, hdl_info.namesize); |
| 1603 | req_count = hdl_info.count; |
| 1604 | count = hdl_info.count = 0; /* the actual no of handles */ |
| 1605 | if (req_count > 0) { |
| 1606 | hib = hdlip = |
| 1607 | kmem_zalloc(req_count * sizeof (struct handle_info), |
| 1608 | KM_SLEEP); |
| 1609 | } else { |
| 1610 | hib = hdlip = 0; |
| 1611 | req_count = hdl_info.count = 0; |
| 1612 | } |
| 1613 | |
| 1614 | /* |
| 1615 | * display existing handles |
| 1616 | */ |
| 1617 | mutex_enter(&bofi_low_mutex); |
| 1618 | mutex_enter(&bofi_mutex); |
| 1619 | for (i = 0; i < HDL_HASH_TBL_SIZE; i++) { |
| 1620 | hhashp = &hhash_table[i]; |
| 1621 | for (hp = hhashp->hnext; hp != hhashp; hp = hp->hnext) { |
| 1622 | if (!driver_under_test(hp->dip) || |
| 1623 | ddi_name_to_major(ddi_get_name(hp->dip)) != |
| 1624 | ddi_name_to_major(namep) || |
| 1625 | ++(hdl_info.count) > req_count || |
| 1626 | count == req_count) |
| 1627 | continue; |
| 1628 | |
| 1629 | hdlip->instance = hp->instance; |
| 1630 | hdlip->rnumber = hp->rnumber; |
| 1631 | switch (hp->type) { |
| 1632 | case BOFI_ACC_HDL: |
| 1633 | hdlip->access_type = BOFI_PIO_RW; |
| 1634 | hdlip->offset = hp->offset; |
| 1635 | hdlip->len = hp->len; |
| 1636 | break; |
| 1637 | case BOFI_DMA_HDL: |
| 1638 | hdlip->access_type = 0; |
| 1639 | if (hp->flags & DDI_DMA_WRITE) |
| 1640 | hdlip->access_type |= |
| 1641 | BOFI_DMA_W; |
| 1642 | if (hp->flags & DDI_DMA_READ) |
| 1643 | hdlip->access_type |= |
| 1644 | BOFI_DMA_R; |
| 1645 | hdlip->len = hp->len; |
| 1646 | hdlip->addr_cookie = |
| 1647 | (uint64_t)(uintptr_t)hp->addr; |
| 1648 | break; |
| 1649 | case BOFI_INT_HDL: |
| 1650 | hdlip->access_type = BOFI_INTR; |
| 1651 | break; |
| 1652 | default: |
| 1653 | hdlip->access_type = 0; |
| 1654 | break; |
| 1655 | } |
| 1656 | hdlip++; |
| 1657 | count++; |
| 1658 | } |
| 1659 | } |
| 1660 | mutex_exit(&bofi_mutex); |
| 1661 | mutex_exit(&bofi_low_mutex); |
| 1662 | err = 0; |
| 1663 | #ifdef _MULTI_DATAMODEL |
| 1664 | switch (ddi_model_convert_from(mode & FMODELS)) { |
| 1665 | case DDI_MODEL_ILP32: |
| 1666 | { |
| 1667 | /* |
| 1668 | * For use when a 32 bit app makes a call into a |
| 1669 | * 64 bit ioctl |
| 1670 | */ |
| 1671 | struct bofi_get_hdl_info32 hdl_info_32; |
| 1672 | |
| 1673 | hdl_info_32.namesize = hdl_info.namesize; |
| 1674 | (void) strncpy(hdl_info_32.name, hdl_info.name, |
| 1675 | NAMESIZE); |
| 1676 | hdl_info_32.count = hdl_info.count; |
| 1677 | hdl_info_32.hdli = (caddr32_t)(uintptr_t)hdl_info.hdli; |
| 1678 | if (ddi_copyout(&hdl_info_32, (void *)arg, |
| 1679 | sizeof (hdl_info_32), mode) != 0) { |
| 1680 | kmem_free(namep, hdl_info.namesize+1); |
| 1681 | if (req_count > 0) |
| 1682 | kmem_free(hib, |
| 1683 | req_count * sizeof (*hib)); |
| 1684 | return (EFAULT); |
| 1685 | } |
| 1686 | break; |
| 1687 | } |
| 1688 | case DDI_MODEL_NONE: |
| 1689 | if (ddi_copyout(&hdl_info, (void *)arg, |
| 1690 | sizeof (hdl_info), mode) != 0) { |
| 1691 | kmem_free(namep, hdl_info.namesize+1); |
| 1692 | if (req_count > 0) |
| 1693 | kmem_free(hib, |
| 1694 | req_count * sizeof (*hib)); |
| 1695 | return (EFAULT); |
| 1696 | } |
| 1697 | break; |
| 1698 | } |
| 1699 | #else /* ! _MULTI_DATAMODEL */ |
| 1700 | if (ddi_copyout(&hdl_info, (void *)arg, |
| 1701 | sizeof (hdl_info), mode) != 0) { |
| 1702 | kmem_free(namep, hdl_info.namesize+1); |
| 1703 | if (req_count > 0) |
| 1704 | kmem_free(hib, req_count * sizeof (*hib)); |
| 1705 | return (EFAULT); |
| 1706 | } |
| 1707 | #endif /* ! _MULTI_DATAMODEL */ |
| 1708 | if (count > 0) { |
| 1709 | if (ddi_copyout(hib, hdl_info.hdli, |
| 1710 | count * sizeof (*hib), mode) != 0) { |
| 1711 | kmem_free(namep, hdl_info.namesize+1); |
| 1712 | if (req_count > 0) |
| 1713 | kmem_free(hib, |
| 1714 | req_count * sizeof (*hib)); |
| 1715 | return (EFAULT); |
| 1716 | } |
| 1717 | } |
| 1718 | kmem_free(namep, hdl_info.namesize+1); |
| 1719 | if (req_count > 0) |
| 1720 | kmem_free(hib, req_count * sizeof (*hib)); |
| 1721 | return (err); |
| 1722 | default: |
| 1723 | return (ENOTTY); |
| 1724 | } |
| 1725 | } |
| 1726 | |
| 1727 | |
| 1728 | /* |
| 1729 | * add a new error definition |
| 1730 | */ |
| 1731 | static int |
| 1732 | bofi_errdef_alloc(struct bofi_errdef *errdefp, char *namep, |
| 1733 | struct bofi_errent *softc) |
| 1734 | { |
| 1735 | struct bofi_errent *ep; |
| 1736 | struct bofi_shadow *hp; |
| 1737 | struct bofi_link *lp; |
| 1738 | |
| 1739 | /* |
| 1740 | * allocate errdef structure and put on in-use list |
| 1741 | */ |
| 1742 | ep = kmem_zalloc(sizeof (struct bofi_errent), KM_SLEEP); |
| 1743 | ep->errdef = *errdefp; |
| 1744 | ep->name = namep; |
| 1745 | ep->errdef.errdef_handle = (uint64_t)(uintptr_t)ep; |
dilpreet | 00d0963 | 2006-04-23 15:26:28 -0700 | [diff] [blame] | 1746 | ep->errstate.severity = DDI_SERVICE_RESTORED; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 1747 | ep->errstate.errdef_handle = (uint64_t)(uintptr_t)ep; |
| 1748 | cv_init(&ep->cv, NULL, CV_DRIVER, NULL); |
| 1749 | /* |
| 1750 | * allocate space for logging |
| 1751 | */ |
| 1752 | ep->errdef.log.entries = 0; |
| 1753 | ep->errdef.log.wrapcnt = 0; |
| 1754 | if (ep->errdef.access_type & BOFI_LOG) |
| 1755 | ep->logbase = kmem_alloc(sizeof (struct acc_log_elem) * |
| 1756 | ep->errdef.log.logsize, KM_SLEEP); |
| 1757 | else |
| 1758 | ep->logbase = NULL; |
| 1759 | /* |
| 1760 | * put on in-use list |
| 1761 | */ |
| 1762 | mutex_enter(&bofi_low_mutex); |
| 1763 | mutex_enter(&bofi_mutex); |
| 1764 | ep->next = errent_listp; |
| 1765 | errent_listp = ep; |
| 1766 | /* |
| 1767 | * and add it to the per-clone list |
| 1768 | */ |
| 1769 | ep->cnext = softc->cnext; |
| 1770 | softc->cnext->cprev = ep; |
| 1771 | ep->cprev = softc; |
| 1772 | softc->cnext = ep; |
| 1773 | |
| 1774 | /* |
| 1775 | * look for corresponding shadow handle structures and if we find any |
| 1776 | * tag this errdef structure on to their link lists. |
| 1777 | */ |
| 1778 | for (hp = shadow_list.next; hp != &shadow_list; hp = hp->next) { |
| 1779 | if (ddi_name_to_major(hp->name) == ddi_name_to_major(namep) && |
| 1780 | hp->instance == errdefp->instance && |
| 1781 | (((errdefp->access_type & BOFI_DMA_RW) && |
| 1782 | (ep->errdef.rnumber == -1 || |
| 1783 | hp->rnumber == ep->errdef.rnumber) && |
| 1784 | hp->type == BOFI_DMA_HDL && |
| 1785 | (((uintptr_t)(hp->addr + ep->errdef.offset + |
| 1786 | ep->errdef.len) & ~LLSZMASK) > |
| 1787 | ((uintptr_t)((hp->addr + ep->errdef.offset) + |
| 1788 | LLSZMASK) & ~LLSZMASK))) || |
| 1789 | ((errdefp->access_type & BOFI_INTR) && |
| 1790 | hp->type == BOFI_INT_HDL) || |
| 1791 | ((errdefp->access_type & BOFI_PIO_RW) && |
| 1792 | hp->type == BOFI_ACC_HDL && |
| 1793 | (errdefp->rnumber == -1 || |
| 1794 | hp->rnumber == errdefp->rnumber) && |
| 1795 | (errdefp->len == 0 || |
| 1796 | hp->offset < errdefp->offset + errdefp->len) && |
| 1797 | hp->offset + hp->len > errdefp->offset))) { |
| 1798 | lp = bofi_link_freelist; |
| 1799 | if (lp != NULL) { |
| 1800 | bofi_link_freelist = lp->link; |
| 1801 | lp->errentp = ep; |
| 1802 | lp->link = hp->link; |
| 1803 | hp->link = lp; |
| 1804 | } |
| 1805 | } |
| 1806 | } |
| 1807 | errdefp->errdef_handle = (uint64_t)(uintptr_t)ep; |
| 1808 | mutex_exit(&bofi_mutex); |
| 1809 | mutex_exit(&bofi_low_mutex); |
| 1810 | ep->softintr_id = NULL; |
| 1811 | return (ddi_add_softintr(our_dip, DDI_SOFTINT_MED, &ep->softintr_id, |
| 1812 | NULL, NULL, bofi_signal, (caddr_t)&ep->errdef)); |
| 1813 | } |
| 1814 | |
| 1815 | |
| 1816 | /* |
| 1817 | * delete existing errdef |
| 1818 | */ |
| 1819 | static int |
| 1820 | bofi_errdef_free(struct bofi_errent *ep) |
| 1821 | { |
| 1822 | struct bofi_errent *hep, *prev_hep; |
| 1823 | struct bofi_link *lp, *prev_lp, *next_lp; |
| 1824 | struct bofi_shadow *hp; |
| 1825 | |
| 1826 | mutex_enter(&bofi_low_mutex); |
| 1827 | mutex_enter(&bofi_mutex); |
| 1828 | /* |
| 1829 | * don't just assume its a valid ep - check that its on the |
| 1830 | * in-use list |
| 1831 | */ |
| 1832 | prev_hep = NULL; |
| 1833 | for (hep = errent_listp; hep != NULL; ) { |
| 1834 | if (hep == ep) |
| 1835 | break; |
| 1836 | prev_hep = hep; |
| 1837 | hep = hep->next; |
| 1838 | } |
| 1839 | if (hep == NULL) { |
| 1840 | mutex_exit(&bofi_mutex); |
| 1841 | mutex_exit(&bofi_low_mutex); |
| 1842 | return (EINVAL); |
| 1843 | } |
| 1844 | /* |
| 1845 | * found it - delete from in-use list |
| 1846 | */ |
| 1847 | |
| 1848 | if (prev_hep) |
| 1849 | prev_hep->next = hep->next; |
| 1850 | else |
| 1851 | errent_listp = hep->next; |
| 1852 | /* |
| 1853 | * and take it off the per-clone list |
| 1854 | */ |
| 1855 | hep->cnext->cprev = hep->cprev; |
| 1856 | hep->cprev->cnext = hep->cnext; |
| 1857 | /* |
| 1858 | * see if we are on any shadow handle link lists - and if we |
| 1859 | * are then take us off |
| 1860 | */ |
| 1861 | for (hp = shadow_list.next; hp != &shadow_list; hp = hp->next) { |
| 1862 | prev_lp = NULL; |
| 1863 | for (lp = hp->link; lp != NULL; ) { |
| 1864 | if (lp->errentp == ep) { |
| 1865 | if (prev_lp) |
| 1866 | prev_lp->link = lp->link; |
| 1867 | else |
| 1868 | hp->link = lp->link; |
| 1869 | next_lp = lp->link; |
| 1870 | lp->link = bofi_link_freelist; |
| 1871 | bofi_link_freelist = lp; |
| 1872 | lp = next_lp; |
| 1873 | } else { |
| 1874 | prev_lp = lp; |
| 1875 | lp = lp->link; |
| 1876 | } |
| 1877 | } |
| 1878 | } |
| 1879 | mutex_exit(&bofi_mutex); |
| 1880 | mutex_exit(&bofi_low_mutex); |
| 1881 | |
| 1882 | cv_destroy(&ep->cv); |
| 1883 | kmem_free(ep->name, ep->errdef.namesize+1); |
| 1884 | if ((ep->errdef.access_type & BOFI_LOG) && |
stephh | e5ba14f | 2007-10-09 15:55:12 -0700 | [diff] [blame] | 1885 | ep->errdef.log.logsize && ep->logbase) /* double check */ |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 1886 | kmem_free(ep->logbase, |
| 1887 | sizeof (struct acc_log_elem) * ep->errdef.log.logsize); |
| 1888 | |
| 1889 | if (ep->softintr_id) |
| 1890 | ddi_remove_softintr(ep->softintr_id); |
| 1891 | kmem_free(ep, sizeof (struct bofi_errent)); |
| 1892 | return (0); |
| 1893 | } |
| 1894 | |
| 1895 | |
| 1896 | /* |
| 1897 | * start all errdefs corresponding to this name and instance |
| 1898 | */ |
| 1899 | static void |
| 1900 | bofi_start(struct bofi_errctl *errctlp, char *namep) |
| 1901 | { |
| 1902 | struct bofi_errent *ep; |
| 1903 | |
| 1904 | /* |
| 1905 | * look for any errdefs with matching name and instance |
| 1906 | */ |
| 1907 | mutex_enter(&bofi_low_mutex); |
| 1908 | for (ep = errent_listp; ep != NULL; ep = ep->next) |
| 1909 | if (strncmp(namep, ep->name, NAMESIZE) == 0 && |
| 1910 | errctlp->instance == ep->errdef.instance) { |
| 1911 | ep->state |= BOFI_DEV_ACTIVE; |
| 1912 | (void) drv_getparm(TIME, &(ep->errdef.log.start_time)); |
| 1913 | ep->errdef.log.stop_time = 0ul; |
| 1914 | } |
| 1915 | mutex_exit(&bofi_low_mutex); |
| 1916 | } |
| 1917 | |
| 1918 | |
| 1919 | /* |
| 1920 | * stop all errdefs corresponding to this name and instance |
| 1921 | */ |
| 1922 | static void |
| 1923 | bofi_stop(struct bofi_errctl *errctlp, char *namep) |
| 1924 | { |
| 1925 | struct bofi_errent *ep; |
| 1926 | |
| 1927 | /* |
| 1928 | * look for any errdefs with matching name and instance |
| 1929 | */ |
| 1930 | mutex_enter(&bofi_low_mutex); |
| 1931 | for (ep = errent_listp; ep != NULL; ep = ep->next) |
| 1932 | if (strncmp(namep, ep->name, NAMESIZE) == 0 && |
| 1933 | errctlp->instance == ep->errdef.instance) { |
| 1934 | ep->state &= ~BOFI_DEV_ACTIVE; |
| 1935 | if (ep->errdef.log.stop_time == 0ul) |
| 1936 | (void) drv_getparm(TIME, |
| 1937 | &(ep->errdef.log.stop_time)); |
| 1938 | } |
| 1939 | mutex_exit(&bofi_low_mutex); |
| 1940 | } |
| 1941 | |
| 1942 | |
| 1943 | /* |
| 1944 | * wake up any thread waiting on this errdefs |
| 1945 | */ |
| 1946 | static uint_t |
| 1947 | bofi_signal(caddr_t arg) |
| 1948 | { |
| 1949 | struct bofi_errdef *edp = (struct bofi_errdef *)arg; |
| 1950 | struct bofi_errent *hep; |
| 1951 | struct bofi_errent *ep = |
| 1952 | (struct bofi_errent *)(uintptr_t)edp->errdef_handle; |
| 1953 | |
| 1954 | mutex_enter(&bofi_low_mutex); |
| 1955 | for (hep = errent_listp; hep != NULL; ) { |
| 1956 | if (hep == ep) |
| 1957 | break; |
| 1958 | hep = hep->next; |
| 1959 | } |
| 1960 | if (hep == NULL) { |
| 1961 | mutex_exit(&bofi_low_mutex); |
| 1962 | return (DDI_INTR_UNCLAIMED); |
| 1963 | } |
| 1964 | if ((ep->errdef.access_type & BOFI_LOG) && |
| 1965 | (edp->log.flags & BOFI_LOG_FULL)) { |
| 1966 | edp->log.stop_time = bofi_gettime(); |
| 1967 | ep->state |= BOFI_NEW_MESSAGE; |
| 1968 | if (ep->state & BOFI_MESSAGE_WAIT) |
| 1969 | cv_broadcast(&ep->cv); |
| 1970 | ep->state &= ~BOFI_MESSAGE_WAIT; |
| 1971 | } |
| 1972 | if (ep->errstate.msg_time != 0) { |
| 1973 | ep->state |= BOFI_NEW_MESSAGE; |
| 1974 | if (ep->state & BOFI_MESSAGE_WAIT) |
| 1975 | cv_broadcast(&ep->cv); |
| 1976 | ep->state &= ~BOFI_MESSAGE_WAIT; |
| 1977 | } |
| 1978 | mutex_exit(&bofi_low_mutex); |
| 1979 | return (DDI_INTR_CLAIMED); |
| 1980 | } |
| 1981 | |
| 1982 | |
| 1983 | /* |
| 1984 | * wake up all errdefs corresponding to this name and instance |
| 1985 | */ |
| 1986 | static void |
| 1987 | bofi_broadcast(struct bofi_errctl *errctlp, char *namep) |
| 1988 | { |
| 1989 | struct bofi_errent *ep; |
| 1990 | |
| 1991 | /* |
| 1992 | * look for any errdefs with matching name and instance |
| 1993 | */ |
| 1994 | mutex_enter(&bofi_low_mutex); |
| 1995 | for (ep = errent_listp; ep != NULL; ep = ep->next) |
| 1996 | if (strncmp(namep, ep->name, NAMESIZE) == 0 && |
| 1997 | errctlp->instance == ep->errdef.instance) { |
| 1998 | /* |
| 1999 | * wake up sleepers |
| 2000 | */ |
| 2001 | ep->state |= BOFI_NEW_MESSAGE; |
| 2002 | if (ep->state & BOFI_MESSAGE_WAIT) |
| 2003 | cv_broadcast(&ep->cv); |
| 2004 | ep->state &= ~BOFI_MESSAGE_WAIT; |
| 2005 | } |
| 2006 | mutex_exit(&bofi_low_mutex); |
| 2007 | } |
| 2008 | |
| 2009 | |
| 2010 | /* |
| 2011 | * clear "acc_chk" for all errdefs corresponding to this name and instance |
| 2012 | * and wake them up. |
| 2013 | */ |
| 2014 | static void |
| 2015 | bofi_clear_acc_chk(struct bofi_errctl *errctlp, char *namep) |
| 2016 | { |
| 2017 | struct bofi_errent *ep; |
| 2018 | |
| 2019 | /* |
| 2020 | * look for any errdefs with matching name and instance |
| 2021 | */ |
| 2022 | mutex_enter(&bofi_low_mutex); |
| 2023 | for (ep = errent_listp; ep != NULL; ep = ep->next) |
| 2024 | if (strncmp(namep, ep->name, NAMESIZE) == 0 && |
| 2025 | errctlp->instance == ep->errdef.instance) { |
| 2026 | mutex_enter(&bofi_mutex); |
| 2027 | if (ep->errdef.access_count == 0 && |
| 2028 | ep->errdef.fail_count == 0) |
| 2029 | ep->errdef.acc_chk = 0; |
| 2030 | mutex_exit(&bofi_mutex); |
| 2031 | /* |
| 2032 | * wake up sleepers |
| 2033 | */ |
| 2034 | ep->state |= BOFI_NEW_MESSAGE; |
| 2035 | if (ep->state & BOFI_MESSAGE_WAIT) |
| 2036 | cv_broadcast(&ep->cv); |
| 2037 | ep->state &= ~BOFI_MESSAGE_WAIT; |
| 2038 | } |
| 2039 | mutex_exit(&bofi_low_mutex); |
| 2040 | } |
| 2041 | |
| 2042 | |
| 2043 | /* |
| 2044 | * set "fail_count" to 0 for all errdefs corresponding to this name and instance |
| 2045 | * whose "access_count" has expired, set "acc_chk" to 0 and wake them up. |
| 2046 | */ |
| 2047 | static void |
| 2048 | bofi_clear_errors(struct bofi_errctl *errctlp, char *namep) |
| 2049 | { |
| 2050 | struct bofi_errent *ep; |
| 2051 | |
| 2052 | /* |
| 2053 | * look for any errdefs with matching name and instance |
| 2054 | */ |
| 2055 | mutex_enter(&bofi_low_mutex); |
| 2056 | for (ep = errent_listp; ep != NULL; ep = ep->next) |
| 2057 | if (strncmp(namep, ep->name, NAMESIZE) == 0 && |
| 2058 | errctlp->instance == ep->errdef.instance) { |
| 2059 | mutex_enter(&bofi_mutex); |
| 2060 | if (ep->errdef.access_count == 0) { |
| 2061 | ep->errdef.acc_chk = 0; |
| 2062 | ep->errdef.fail_count = 0; |
| 2063 | mutex_exit(&bofi_mutex); |
| 2064 | if (ep->errdef.log.stop_time == 0ul) |
| 2065 | (void) drv_getparm(TIME, |
| 2066 | &(ep->errdef.log.stop_time)); |
| 2067 | } else |
| 2068 | mutex_exit(&bofi_mutex); |
| 2069 | /* |
| 2070 | * wake up sleepers |
| 2071 | */ |
| 2072 | ep->state |= BOFI_NEW_MESSAGE; |
| 2073 | if (ep->state & BOFI_MESSAGE_WAIT) |
| 2074 | cv_broadcast(&ep->cv); |
| 2075 | ep->state &= ~BOFI_MESSAGE_WAIT; |
| 2076 | } |
| 2077 | mutex_exit(&bofi_low_mutex); |
| 2078 | } |
| 2079 | |
| 2080 | |
| 2081 | /* |
| 2082 | * set "access_count" and "fail_count" to 0 for all errdefs corresponding to |
| 2083 | * this name and instance, set "acc_chk" to 0, and wake them up. |
| 2084 | */ |
| 2085 | static void |
| 2086 | bofi_clear_errdefs(struct bofi_errctl *errctlp, char *namep) |
| 2087 | { |
| 2088 | struct bofi_errent *ep; |
| 2089 | |
| 2090 | /* |
| 2091 | * look for any errdefs with matching name and instance |
| 2092 | */ |
| 2093 | mutex_enter(&bofi_low_mutex); |
| 2094 | for (ep = errent_listp; ep != NULL; ep = ep->next) |
| 2095 | if (strncmp(namep, ep->name, NAMESIZE) == 0 && |
| 2096 | errctlp->instance == ep->errdef.instance) { |
| 2097 | mutex_enter(&bofi_mutex); |
| 2098 | ep->errdef.acc_chk = 0; |
| 2099 | ep->errdef.access_count = 0; |
| 2100 | ep->errdef.fail_count = 0; |
| 2101 | mutex_exit(&bofi_mutex); |
| 2102 | if (ep->errdef.log.stop_time == 0ul) |
| 2103 | (void) drv_getparm(TIME, |
| 2104 | &(ep->errdef.log.stop_time)); |
| 2105 | /* |
| 2106 | * wake up sleepers |
| 2107 | */ |
| 2108 | ep->state |= BOFI_NEW_MESSAGE; |
| 2109 | if (ep->state & BOFI_MESSAGE_WAIT) |
| 2110 | cv_broadcast(&ep->cv); |
| 2111 | ep->state &= ~BOFI_MESSAGE_WAIT; |
| 2112 | } |
| 2113 | mutex_exit(&bofi_low_mutex); |
| 2114 | } |
| 2115 | |
| 2116 | |
| 2117 | /* |
| 2118 | * get state for this errdef |
| 2119 | */ |
| 2120 | static int |
| 2121 | bofi_errdef_check(struct bofi_errstate *errstatep, struct acc_log_elem **logpp) |
| 2122 | { |
| 2123 | struct bofi_errent *hep; |
| 2124 | struct bofi_errent *ep; |
| 2125 | |
| 2126 | ep = (struct bofi_errent *)(uintptr_t)errstatep->errdef_handle; |
| 2127 | mutex_enter(&bofi_low_mutex); |
| 2128 | /* |
| 2129 | * don't just assume its a valid ep - check that its on the |
| 2130 | * in-use list |
| 2131 | */ |
| 2132 | for (hep = errent_listp; hep != NULL; hep = hep->next) |
| 2133 | if (hep == ep) |
| 2134 | break; |
| 2135 | if (hep == NULL) { |
| 2136 | mutex_exit(&bofi_low_mutex); |
| 2137 | return (EINVAL); |
| 2138 | } |
| 2139 | mutex_enter(&bofi_mutex); |
| 2140 | ep->errstate.access_count = ep->errdef.access_count; |
| 2141 | ep->errstate.fail_count = ep->errdef.fail_count; |
| 2142 | ep->errstate.acc_chk = ep->errdef.acc_chk; |
| 2143 | ep->errstate.log = ep->errdef.log; |
| 2144 | *logpp = ep->logbase; |
| 2145 | *errstatep = ep->errstate; |
| 2146 | mutex_exit(&bofi_mutex); |
| 2147 | mutex_exit(&bofi_low_mutex); |
| 2148 | return (0); |
| 2149 | } |
| 2150 | |
| 2151 | |
| 2152 | /* |
| 2153 | * Wait for a ddi_report_fault message to come back for this errdef |
| 2154 | * Then return state for this errdef. |
| 2155 | * fault report is intercepted by bofi_post_event, which triggers |
| 2156 | * bofi_signal via a softint, which will wake up this routine if |
| 2157 | * we are waiting |
| 2158 | */ |
| 2159 | static int |
| 2160 | bofi_errdef_check_w(struct bofi_errstate *errstatep, |
| 2161 | struct acc_log_elem **logpp) |
| 2162 | { |
| 2163 | struct bofi_errent *hep; |
| 2164 | struct bofi_errent *ep; |
| 2165 | int rval = 0; |
| 2166 | |
| 2167 | ep = (struct bofi_errent *)(uintptr_t)errstatep->errdef_handle; |
| 2168 | mutex_enter(&bofi_low_mutex); |
| 2169 | retry: |
| 2170 | /* |
| 2171 | * don't just assume its a valid ep - check that its on the |
| 2172 | * in-use list |
| 2173 | */ |
| 2174 | for (hep = errent_listp; hep != NULL; hep = hep->next) |
| 2175 | if (hep == ep) |
| 2176 | break; |
| 2177 | if (hep == NULL) { |
| 2178 | mutex_exit(&bofi_low_mutex); |
| 2179 | return (EINVAL); |
| 2180 | } |
| 2181 | /* |
| 2182 | * wait for ddi_report_fault for the devinfo corresponding |
| 2183 | * to this errdef |
| 2184 | */ |
| 2185 | if (rval == 0 && !(ep->state & BOFI_NEW_MESSAGE)) { |
| 2186 | ep->state |= BOFI_MESSAGE_WAIT; |
dilpreet | 00d0963 | 2006-04-23 15:26:28 -0700 | [diff] [blame] | 2187 | if (cv_wait_sig(&ep->cv, &bofi_low_mutex) == 0) { |
| 2188 | if (!(ep->state & BOFI_NEW_MESSAGE)) |
| 2189 | rval = EINTR; |
| 2190 | } |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 2191 | goto retry; |
| 2192 | } |
| 2193 | ep->state &= ~BOFI_NEW_MESSAGE; |
| 2194 | /* |
| 2195 | * we either didn't need to sleep, we've been woken up or we've been |
| 2196 | * signaled - either way return state now |
| 2197 | */ |
| 2198 | mutex_enter(&bofi_mutex); |
| 2199 | ep->errstate.access_count = ep->errdef.access_count; |
| 2200 | ep->errstate.fail_count = ep->errdef.fail_count; |
| 2201 | ep->errstate.acc_chk = ep->errdef.acc_chk; |
| 2202 | ep->errstate.log = ep->errdef.log; |
| 2203 | *logpp = ep->logbase; |
| 2204 | *errstatep = ep->errstate; |
| 2205 | mutex_exit(&bofi_mutex); |
| 2206 | mutex_exit(&bofi_low_mutex); |
| 2207 | return (rval); |
| 2208 | } |
| 2209 | |
| 2210 | |
| 2211 | /* |
| 2212 | * support routine - check if requested driver is defined as under test in the |
| 2213 | * conf file. |
| 2214 | */ |
| 2215 | static int |
| 2216 | driver_under_test(dev_info_t *rdip) |
| 2217 | { |
| 2218 | int i; |
| 2219 | char *rname; |
| 2220 | major_t rmaj; |
| 2221 | |
| 2222 | rname = ddi_get_name(rdip); |
| 2223 | rmaj = ddi_name_to_major(rname); |
| 2224 | |
| 2225 | /* |
| 2226 | * Enforce the user to specifically request the following drivers. |
| 2227 | */ |
| 2228 | for (i = 0; i < driver_list_size; i += (1 + strlen(&driver_list[i]))) { |
| 2229 | if (driver_list_neg == 0) { |
| 2230 | if (rmaj == ddi_name_to_major(&driver_list[i])) |
| 2231 | return (1); |
| 2232 | } else { |
| 2233 | if (rmaj == ddi_name_to_major(&driver_list[i+1])) |
| 2234 | return (0); |
| 2235 | } |
| 2236 | } |
| 2237 | if (driver_list_neg == 0) |
| 2238 | return (0); |
| 2239 | else |
| 2240 | return (1); |
| 2241 | |
| 2242 | } |
| 2243 | |
| 2244 | |
| 2245 | static void |
| 2246 | log_acc_event(struct bofi_errent *ep, uint_t at, offset_t offset, off_t len, |
| 2247 | size_t repcount, uint64_t *valuep) |
| 2248 | { |
| 2249 | struct bofi_errdef *edp = &(ep->errdef); |
| 2250 | struct acc_log *log = &edp->log; |
| 2251 | |
| 2252 | ASSERT(log != NULL); |
| 2253 | ASSERT(MUTEX_HELD(&bofi_mutex)); |
| 2254 | |
| 2255 | if (log->flags & BOFI_LOG_REPIO) |
| 2256 | repcount = 1; |
| 2257 | else if (repcount == 0 && edp->access_count > 0 && |
stephh | e5ba14f | 2007-10-09 15:55:12 -0700 | [diff] [blame] | 2258 | (log->flags & BOFI_LOG_FULL) == 0) |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 2259 | edp->access_count += 1; |
| 2260 | |
| 2261 | if (repcount && log->entries < log->logsize) { |
| 2262 | struct acc_log_elem *elem = ep->logbase + log->entries; |
| 2263 | |
| 2264 | if (log->flags & BOFI_LOG_TIMESTAMP) |
| 2265 | elem->access_time = bofi_gettime(); |
| 2266 | elem->access_type = at; |
| 2267 | elem->offset = offset; |
| 2268 | elem->value = valuep ? *valuep : 0ll; |
| 2269 | elem->size = len; |
| 2270 | elem->repcount = repcount; |
| 2271 | ++log->entries; |
| 2272 | if (log->entries == log->logsize) { |
| 2273 | log->flags |= BOFI_LOG_FULL; |
| 2274 | ddi_trigger_softintr(((struct bofi_errent *) |
| 2275 | (uintptr_t)edp->errdef_handle)->softintr_id); |
| 2276 | } |
| 2277 | } |
| 2278 | if ((log->flags & BOFI_LOG_WRAP) && edp->access_count <= 1) { |
| 2279 | log->wrapcnt++; |
| 2280 | edp->access_count = log->logsize; |
| 2281 | log->entries = 0; /* wrap back to the start */ |
| 2282 | } |
| 2283 | } |
| 2284 | |
| 2285 | |
| 2286 | /* |
| 2287 | * got a condition match on dma read/write - check counts and corrupt |
| 2288 | * data if necessary |
| 2289 | * |
| 2290 | * bofi_mutex always held when this is called. |
| 2291 | */ |
| 2292 | static void |
| 2293 | do_dma_corrupt(struct bofi_shadow *hp, struct bofi_errent *ep, |
| 2294 | uint_t synctype, off_t off, off_t length) |
| 2295 | { |
| 2296 | uint64_t operand; |
| 2297 | int i; |
| 2298 | off_t len; |
| 2299 | caddr_t logaddr; |
| 2300 | uint64_t *addr; |
| 2301 | uint64_t *endaddr; |
dilpreet | 00d0963 | 2006-04-23 15:26:28 -0700 | [diff] [blame] | 2302 | ddi_dma_impl_t *hdlp; |
| 2303 | ndi_err_t *errp; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 2304 | |
| 2305 | ASSERT(MUTEX_HELD(&bofi_mutex)); |
| 2306 | if ((ep->errdef.access_count || |
stephh | e5ba14f | 2007-10-09 15:55:12 -0700 | [diff] [blame] | 2307 | ep->errdef.fail_count) && |
| 2308 | (ep->errdef.access_type & BOFI_LOG)) { |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 2309 | uint_t atype; |
| 2310 | |
| 2311 | if (synctype == DDI_DMA_SYNC_FORDEV) |
| 2312 | atype = BOFI_DMA_W; |
| 2313 | else if (synctype == DDI_DMA_SYNC_FORCPU || |
stephh | e5ba14f | 2007-10-09 15:55:12 -0700 | [diff] [blame] | 2314 | synctype == DDI_DMA_SYNC_FORKERNEL) |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 2315 | atype = BOFI_DMA_R; |
| 2316 | else |
| 2317 | atype = 0; |
| 2318 | if ((off <= ep->errdef.offset && |
stephh | e5ba14f | 2007-10-09 15:55:12 -0700 | [diff] [blame] | 2319 | off + length > ep->errdef.offset) || |
| 2320 | (off > ep->errdef.offset && |
| 2321 | off < ep->errdef.offset + ep->errdef.len)) { |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 2322 | logaddr = (caddr_t)((uintptr_t)(hp->addr + |
| 2323 | off + LLSZMASK) & ~LLSZMASK); |
| 2324 | |
| 2325 | log_acc_event(ep, atype, logaddr - hp->addr, |
| 2326 | length, 1, 0); |
| 2327 | } |
| 2328 | } |
| 2329 | if (ep->errdef.access_count > 1) { |
| 2330 | ep->errdef.access_count--; |
| 2331 | } else if (ep->errdef.fail_count > 0) { |
| 2332 | ep->errdef.fail_count--; |
| 2333 | ep->errdef.access_count = 0; |
| 2334 | /* |
| 2335 | * OK do the corruption |
| 2336 | */ |
| 2337 | if (ep->errstate.fail_time == 0) |
| 2338 | ep->errstate.fail_time = bofi_gettime(); |
| 2339 | /* |
| 2340 | * work out how much to corrupt |
| 2341 | * |
| 2342 | * Make sure endaddr isn't greater than hp->addr + hp->len. |
| 2343 | * If endaddr becomes less than addr len becomes negative |
| 2344 | * and the following loop isn't entered. |
| 2345 | */ |
| 2346 | addr = (uint64_t *)((uintptr_t)((hp->addr + |
| 2347 | ep->errdef.offset) + LLSZMASK) & ~LLSZMASK); |
| 2348 | endaddr = (uint64_t *)((uintptr_t)(hp->addr + min(hp->len, |
| 2349 | ep->errdef.offset + ep->errdef.len)) & ~LLSZMASK); |
| 2350 | len = endaddr - addr; |
| 2351 | operand = ep->errdef.operand; |
dilpreet | 00d0963 | 2006-04-23 15:26:28 -0700 | [diff] [blame] | 2352 | hdlp = (ddi_dma_impl_t *)(hp->hdl.dma_handle); |
| 2353 | errp = &hdlp->dmai_error; |
| 2354 | if (ep->errdef.acc_chk & 2) { |
| 2355 | uint64_t ena; |
| 2356 | char buf[FM_MAX_CLASS]; |
| 2357 | |
| 2358 | errp->err_status = DDI_FM_NONFATAL; |
| 2359 | (void) snprintf(buf, FM_MAX_CLASS, FM_SIMULATED_DMA); |
| 2360 | ena = fm_ena_generate(0, FM_ENA_FMT1); |
| 2361 | ddi_fm_ereport_post(hp->dip, buf, ena, |
| 2362 | DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, |
| 2363 | FM_EREPORT_VERS0, NULL); |
| 2364 | } |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 2365 | switch (ep->errdef.optype) { |
| 2366 | case BOFI_EQUAL : |
| 2367 | for (i = 0; i < len; i++) |
| 2368 | *(addr + i) = operand; |
| 2369 | break; |
| 2370 | case BOFI_AND : |
| 2371 | for (i = 0; i < len; i++) |
| 2372 | *(addr + i) &= operand; |
| 2373 | break; |
| 2374 | case BOFI_OR : |
| 2375 | for (i = 0; i < len; i++) |
| 2376 | *(addr + i) |= operand; |
| 2377 | break; |
| 2378 | case BOFI_XOR : |
| 2379 | for (i = 0; i < len; i++) |
| 2380 | *(addr + i) ^= operand; |
| 2381 | break; |
| 2382 | default: |
| 2383 | /* do nothing */ |
| 2384 | break; |
| 2385 | } |
| 2386 | } |
| 2387 | } |
| 2388 | |
| 2389 | |
| 2390 | static uint64_t do_bofi_rd8(struct bofi_shadow *, caddr_t); |
| 2391 | static uint64_t do_bofi_rd16(struct bofi_shadow *, caddr_t); |
| 2392 | static uint64_t do_bofi_rd32(struct bofi_shadow *, caddr_t); |
| 2393 | static uint64_t do_bofi_rd64(struct bofi_shadow *, caddr_t); |
| 2394 | |
| 2395 | |
| 2396 | /* |
| 2397 | * check all errdefs linked to this shadow handle. If we've got a condition |
| 2398 | * match check counts and corrupt data if necessary |
| 2399 | * |
| 2400 | * bofi_mutex always held when this is called. |
| 2401 | * |
| 2402 | * because of possibility of BOFI_NO_TRANSFER, we couldn't get data |
| 2403 | * from io-space before calling this, so we pass in the func to do the |
| 2404 | * transfer as a parameter. |
| 2405 | */ |
| 2406 | static uint64_t |
| 2407 | do_pior_corrupt(struct bofi_shadow *hp, caddr_t addr, |
| 2408 | uint64_t (*func)(), size_t repcount, size_t accsize) |
| 2409 | { |
| 2410 | struct bofi_errent *ep; |
| 2411 | struct bofi_link *lp; |
| 2412 | uint64_t operand; |
| 2413 | uintptr_t minlen; |
| 2414 | intptr_t base; |
| 2415 | int done_get = 0; |
| 2416 | uint64_t get_val, gv; |
dilpreet | 00d0963 | 2006-04-23 15:26:28 -0700 | [diff] [blame] | 2417 | ddi_acc_impl_t *hdlp; |
| 2418 | ndi_err_t *errp; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 2419 | |
| 2420 | ASSERT(MUTEX_HELD(&bofi_mutex)); |
| 2421 | /* |
| 2422 | * check through all errdefs associated with this shadow handle |
| 2423 | */ |
| 2424 | for (lp = hp->link; lp != NULL; lp = lp->link) { |
| 2425 | ep = lp->errentp; |
| 2426 | if (ep->errdef.len == 0) |
| 2427 | minlen = hp->len; |
| 2428 | else |
| 2429 | minlen = min(hp->len, ep->errdef.len); |
| 2430 | base = addr - hp->addr - ep->errdef.offset + hp->offset; |
| 2431 | if ((ep->errdef.access_type & BOFI_PIO_R) && |
| 2432 | (ep->state & BOFI_DEV_ACTIVE) && |
| 2433 | base >= 0 && base < minlen) { |
| 2434 | /* |
| 2435 | * condition match for pio read |
| 2436 | */ |
| 2437 | if (ep->errdef.access_count > 1) { |
| 2438 | ep->errdef.access_count--; |
| 2439 | if (done_get == 0) { |
| 2440 | done_get = 1; |
| 2441 | gv = get_val = func(hp, addr); |
| 2442 | } |
| 2443 | if (ep->errdef.access_type & BOFI_LOG) { |
| 2444 | log_acc_event(ep, BOFI_PIO_R, |
| 2445 | addr - hp->addr, |
| 2446 | accsize, repcount, &gv); |
| 2447 | } |
| 2448 | } else if (ep->errdef.fail_count > 0) { |
| 2449 | ep->errdef.fail_count--; |
| 2450 | ep->errdef.access_count = 0; |
| 2451 | /* |
| 2452 | * OK do corruption |
| 2453 | */ |
| 2454 | if (ep->errstate.fail_time == 0) |
| 2455 | ep->errstate.fail_time = bofi_gettime(); |
| 2456 | operand = ep->errdef.operand; |
| 2457 | if (done_get == 0) { |
| 2458 | if (ep->errdef.optype == |
| 2459 | BOFI_NO_TRANSFER) |
| 2460 | /* |
| 2461 | * no transfer - bomb out |
| 2462 | */ |
| 2463 | return (operand); |
| 2464 | done_get = 1; |
| 2465 | gv = get_val = func(hp, addr); |
| 2466 | |
| 2467 | } |
| 2468 | if (ep->errdef.access_type & BOFI_LOG) { |
| 2469 | log_acc_event(ep, BOFI_PIO_R, |
| 2470 | addr - hp->addr, |
| 2471 | accsize, repcount, &gv); |
| 2472 | } |
dilpreet | 00d0963 | 2006-04-23 15:26:28 -0700 | [diff] [blame] | 2473 | hdlp = (ddi_acc_impl_t *)(hp->hdl.acc_handle); |
| 2474 | errp = hdlp->ahi_err; |
| 2475 | if (ep->errdef.acc_chk & 1) { |
| 2476 | uint64_t ena; |
| 2477 | char buf[FM_MAX_CLASS]; |
| 2478 | |
| 2479 | errp->err_status = DDI_FM_NONFATAL; |
| 2480 | (void) snprintf(buf, FM_MAX_CLASS, |
| 2481 | FM_SIMULATED_PIO); |
| 2482 | ena = fm_ena_generate(0, FM_ENA_FMT1); |
| 2483 | ddi_fm_ereport_post(hp->dip, buf, ena, |
| 2484 | DDI_NOSLEEP, FM_VERSION, |
| 2485 | DATA_TYPE_UINT8, FM_EREPORT_VERS0, |
| 2486 | NULL); |
| 2487 | } |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 2488 | switch (ep->errdef.optype) { |
| 2489 | case BOFI_EQUAL : |
| 2490 | get_val = operand; |
| 2491 | break; |
| 2492 | case BOFI_AND : |
| 2493 | get_val &= operand; |
| 2494 | break; |
| 2495 | case BOFI_OR : |
| 2496 | get_val |= operand; |
| 2497 | break; |
| 2498 | case BOFI_XOR : |
| 2499 | get_val ^= operand; |
| 2500 | break; |
| 2501 | default: |
| 2502 | /* do nothing */ |
| 2503 | break; |
| 2504 | } |
| 2505 | } |
| 2506 | } |
| 2507 | } |
| 2508 | if (done_get == 0) |
| 2509 | return (func(hp, addr)); |
| 2510 | else |
| 2511 | return (get_val); |
| 2512 | } |
| 2513 | |
| 2514 | |
| 2515 | /* |
| 2516 | * check all errdefs linked to this shadow handle. If we've got a condition |
| 2517 | * match check counts and corrupt data if necessary |
| 2518 | * |
| 2519 | * bofi_mutex always held when this is called. |
| 2520 | * |
| 2521 | * because of possibility of BOFI_NO_TRANSFER, we return 0 if no data |
| 2522 | * is to be written out to io-space, 1 otherwise |
| 2523 | */ |
| 2524 | static int |
| 2525 | do_piow_corrupt(struct bofi_shadow *hp, caddr_t addr, uint64_t *valuep, |
| 2526 | size_t size, size_t repcount) |
| 2527 | { |
| 2528 | struct bofi_errent *ep; |
| 2529 | struct bofi_link *lp; |
| 2530 | uintptr_t minlen; |
| 2531 | intptr_t base; |
| 2532 | uint64_t v = *valuep; |
dilpreet | 00d0963 | 2006-04-23 15:26:28 -0700 | [diff] [blame] | 2533 | ddi_acc_impl_t *hdlp; |
| 2534 | ndi_err_t *errp; |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 2535 | |
| 2536 | ASSERT(MUTEX_HELD(&bofi_mutex)); |
| 2537 | /* |
| 2538 | * check through all errdefs associated with this shadow handle |
| 2539 | */ |
| 2540 | for (lp = hp->link; lp != NULL; lp = lp->link) { |
| 2541 | ep = lp->errentp; |
| 2542 | if (ep->errdef.len == 0) |
| 2543 | minlen = hp->len; |
| 2544 | else |
| 2545 | minlen = min(hp->len, ep->errdef.len); |
| 2546 | base = (caddr_t)addr - hp->addr - ep->errdef.offset +hp->offset; |
| 2547 | if ((ep->errdef.access_type & BOFI_PIO_W) && |
| 2548 | (ep->state & BOFI_DEV_ACTIVE) && |
| 2549 | base >= 0 && base < minlen) { |
| 2550 | /* |
| 2551 | * condition match for pio write |
| 2552 | */ |
| 2553 | |
| 2554 | if (ep->errdef.access_count > 1) { |
| 2555 | ep->errdef.access_count--; |
| 2556 | if (ep->errdef.access_type & BOFI_LOG) |
| 2557 | log_acc_event(ep, BOFI_PIO_W, |
| 2558 | addr - hp->addr, size, |
| 2559 | repcount, &v); |
| 2560 | } else if (ep->errdef.fail_count > 0) { |
| 2561 | ep->errdef.fail_count--; |
| 2562 | ep->errdef.access_count = 0; |
| 2563 | if (ep->errdef.access_type & BOFI_LOG) |
| 2564 | log_acc_event(ep, BOFI_PIO_W, |
| 2565 | addr - hp->addr, size, |
| 2566 | repcount, &v); |
| 2567 | /* |
| 2568 | * OK do corruption |
| 2569 | */ |
| 2570 | if (ep->errstate.fail_time == 0) |
| 2571 | ep->errstate.fail_time = bofi_gettime(); |
dilpreet | 00d0963 | 2006-04-23 15:26:28 -0700 | [diff] [blame] | 2572 | hdlp = (ddi_acc_impl_t *)(hp->hdl.acc_handle); |
| 2573 | errp = hdlp->ahi_err; |
| 2574 | if (ep->errdef.acc_chk & 1) { |
| 2575 | uint64_t ena; |
| 2576 | char buf[FM_MAX_CLASS]; |
| 2577 | |
| 2578 | errp->err_status = DDI_FM_NONFATAL; |
| 2579 | (void) snprintf(buf, FM_MAX_CLASS, |
| 2580 | FM_SIMULATED_PIO); |
| 2581 | ena = fm_ena_generate(0, FM_ENA_FMT1); |
| 2582 | ddi_fm_ereport_post(hp->dip, buf, ena, |
| 2583 | DDI_NOSLEEP, FM_VERSION, |
| 2584 | DATA_TYPE_UINT8, FM_EREPORT_VERS0, |
| 2585 | NULL); |
| 2586 | } |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 2587 | switch (ep->errdef.optype) { |
| 2588 | case BOFI_EQUAL : |
| 2589 | *valuep = ep->errdef.operand; |
| 2590 | break; |
| 2591 | case BOFI_AND : |
| 2592 | *valuep &= ep->errdef.operand; |
| 2593 | break; |
| 2594 | case BOFI_OR : |
| 2595 | *valuep |= ep->errdef.operand; |
| 2596 | break; |
| 2597 | case BOFI_XOR : |
| 2598 | *valuep ^= ep->errdef.operand; |
| 2599 | break; |
| 2600 | case BOFI_NO_TRANSFER : |
| |