1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * I/O support routines for DR 30 */ 31 32 #include <sys/debug.h> 33 #include <sys/types.h> 34 #include <sys/errno.h> 35 #include <sys/cred.h> 36 #include <sys/dditypes.h> 37 #include <sys/devops.h> 38 #include <sys/modctl.h> 39 #include <sys/poll.h> 40 #include <sys/conf.h> 41 #include <sys/ddi.h> 42 #include <sys/sunddi.h> 43 #include <sys/sunndi.h> 44 #include <sys/ndi_impldefs.h> 45 #include <sys/stat.h> 46 #include <sys/kmem.h> 47 #include <sys/processor.h> 48 #include <sys/cpuvar.h> 49 #include <sys/mem_config.h> 50 #include <sys/promif.h> 51 #include <sys/x_call.h> 52 #include <sys/cpu_sgnblk_defs.h> 53 #include <sys/membar.h> 54 #include <sys/stack.h> 55 #include <sys/sysmacros.h> 56 #include <sys/machsystm.h> 57 #include <sys/spitregs.h> 58 #include <sys/cpupart.h> 59 60 #include <sys/archsystm.h> 61 #include <vm/hat_sfmmu.h> 62 #include <sys/pte.h> 63 #include <sys/mmu.h> 64 #include <sys/x_call.h> 65 #include <sys/cpu_module.h> 66 67 #include <sys/cmn_err.h> 68 69 #include <sys/dr.h> 70 #include <sys/dr_util.h> 71 #include <sys/drmach.h> 72 73 void 74 dr_init_io_unit(dr_io_unit_t *ip) 75 { 76 dr_state_t new_state; 77 78 if (DR_DEV_IS_ATTACHED(&ip->sbi_cm)) { 79 new_state = DR_STATE_CONFIGURED; 80 ip->sbi_cm.sbdev_cond = SBD_COND_OK; 81 } else if (DR_DEV_IS_PRESENT(&ip->sbi_cm)) { 82 new_state = DR_STATE_CONNECTED; 83 ip->sbi_cm.sbdev_cond = SBD_COND_OK; 84 } else { 85 new_state = DR_STATE_EMPTY; 86 } 87 dr_device_transition(&ip->sbi_cm, new_state); 88 } 89 90 /*ARGSUSED*/ 91 void 92 dr_attach_io(dr_handle_t *hp, dr_common_unit_t *cp) 93 { 94 sbd_error_t *err; 95 96 dr_lock_status(hp->h_bd); 97 err = drmach_configure(cp->sbdev_id, 0); 98 dr_unlock_status(hp->h_bd); 99 100 if (!err) 101 err = drmach_io_post_attach(cp->sbdev_id); 102 103 if (err) 104 DRERR_SET_C(&cp->sbdev_error, &err); 105 } 106 107 /* 108 * remove device nodes for the branch indicated by cp 109 */ 110 /*ARGSUSED*/ 111 void 112 dr_detach_io(dr_handle_t *hp, dr_common_unit_t *cp) 113 { 114 sbd_error_t *err; 115 116 err = drmach_unconfigure(cp->sbdev_id, 0); 117 118 if (!err) 119 err = drmach_unconfigure(cp->sbdev_id, DEVI_BRANCH_DESTROY); 120 121 if (!err) 122 err = drmach_io_post_release(cp->sbdev_id); 123 124 if (err) { 125 dr_device_transition(cp, DR_STATE_CONFIGURED); 126 DRERR_SET_C(&cp->sbdev_error, &err); 127 } 128 } 129 130 /*ARGSUSED*/ 131 int 132 dr_disconnect_io(dr_io_unit_t *ip) 133 { 134 return (0); 135 } 136 137 /*ARGSUSED*/ 138 int 139 dr_pre_attach_io(dr_handle_t *hp, 140 dr_common_unit_t **devlist, int devnum) 141 { 142 int d; 143 144 for (d = 0; d < devnum; d++) { 145 dr_common_unit_t *cp = devlist[d]; 146 147 cmn_err(CE_CONT, "OS configure %s", cp->sbdev_path); 148 } 149 150 return (0); 151 } 152 153 /*ARGSUSED*/ 154 int 155 dr_post_attach_io(dr_handle_t *hp, 156 dr_common_unit_t **devlist, int devnum) 157 { 158 return (0); 159 } 160 161 static int 162 dr_check_io_refs(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum) 163 { 164 register int i, reftotal = 0; 165 static fn_t f = "dr_check_io_refs"; 166 167 for (i = 0; i < devnum; i++) { 168 dr_io_unit_t *ip = (dr_io_unit_t *)devlist[i]; 169 dev_info_t *dip; 170 int ref; 171 sbd_error_t *err; 172 173 err = drmach_get_dip(ip->sbi_cm.sbdev_id, &dip); 174 if (err) 175 DRERR_SET_C(&ip->sbi_cm.sbdev_error, &err); 176 else if (dip != NULL) { 177 ref = 0; 178 ASSERT(e_ddi_branch_held(dip)); 179 dr_check_devices(dip, &ref, hp, NULL, NULL, 0); 180 hp->h_err = NULL; 181 if (ref) { 182 dr_dev_err(CE_WARN, &ip->sbi_cm, ESBD_BUSY); 183 } 184 PR_IO("%s: dip(%s) ref = %d\n", 185 f, ddi_get_name(dip), ref); 186 reftotal += ref; 187 } else { 188 PR_IO("%s: NO dip for id (0x%x)\n", 189 f, (uint_t)(uintptr_t)ip->sbi_cm.sbdev_id); 190 } 191 } 192 193 return (reftotal); 194 } 195 196 int 197 dr_pre_release_io(dr_handle_t *hp, 198 dr_common_unit_t **devlist, int devnum) 199 { 200 static fn_t f = "dr_pre_release_io"; 201 int d; 202 203 ASSERT(devnum > 0); 204 205 /* fail if any I/O device pre-release fails */ 206 for (d = 0; d < devnum; d++) { 207 dr_io_unit_t *ip = (dr_io_unit_t *)devlist[d]; 208 209 if ((hp->h_err = drmach_io_pre_release( 210 ip->sbi_cm.sbdev_id)) != 0) { 211 return (-1); 212 } 213 } 214 215 for (d = 0; d < devnum; d++) { 216 dr_io_unit_t *ip = (dr_io_unit_t *)devlist[d]; 217 sbd_error_t *err; 218 219 err = drmach_release(ip->sbi_cm.sbdev_id); 220 if (err) { 221 DRERR_SET_C(&ip->sbi_cm.sbdev_error, 222 &err); 223 return (-1); 224 } 225 } 226 227 /* fail if any I/O devices are still referenced */ 228 if (dr_check_io_refs(hp, devlist, devnum) > 0) { 229 PR_IO("%s: failed - I/O devices ref'd\n", f); 230 231 /* recover before return error */ 232 for (d = 0; d < devnum; d++) { 233 dr_io_unit_t *ip = (dr_io_unit_t *)devlist[d]; 234 sbd_error_t *err; 235 err = drmach_io_unrelease(ip->sbi_cm.sbdev_id); 236 if (err) { 237 DRERR_SET_C(&ip->sbi_cm.sbdev_error, &err); 238 return (-1); 239 } 240 } 241 return (-1); 242 } 243 return (0); 244 } 245 246 /*ARGSUSED*/ 247 int 248 dr_pre_detach_io(dr_handle_t *hp, 249 dr_common_unit_t **devlist, int devnum) 250 { 251 int d; 252 253 ASSERT(devnum > 0); 254 255 for (d = 0; d < devnum; d++) { 256 dr_common_unit_t *cp = devlist[d]; 257 258 cmn_err(CE_CONT, "OS unconfigure %s", cp->sbdev_path); 259 } 260 261 return (0); 262 } 263 264 /*ARGSUSED*/ 265 int 266 dr_post_detach_io(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum) 267 { 268 register int i; 269 int rv = 0; 270 static fn_t f = "dr_post_detach_io"; 271 272 ASSERT(devnum > 0); 273 for (i = 0; i < devnum; i++) { 274 dr_common_unit_t *cp = devlist[i]; 275 if (cp->sbdev_error != NULL) { 276 PR_IO("%s: Failed\n", f); 277 rv = -1; 278 break; 279 } 280 } 281 return (rv); 282 } 283 284 static void 285 dr_get_comp_cond(dr_io_unit_t *ip, dev_info_t *dip) 286 { 287 if (dip == NULL) { 288 ip->sbi_cm.sbdev_cond = SBD_COND_UNKNOWN; 289 return; 290 } 291 292 if (DEVI(dip)->devi_flags & DEVI_RETIRED) { 293 ip->sbi_cm.sbdev_cond = SBD_COND_FAILED; 294 return; 295 } 296 297 if (DR_DEV_IS_ATTACHED(&ip->sbi_cm)) { 298 ip->sbi_cm.sbdev_cond = SBD_COND_OK; 299 } else if (DR_DEV_IS_PRESENT(&ip->sbi_cm)) { 300 ip->sbi_cm.sbdev_cond = SBD_COND_OK; 301 } 302 } 303 304 int 305 dr_io_status(dr_handle_t *hp, dr_devset_t devset, sbd_dev_stat_t *dsp) 306 { 307 int i, ix; 308 dr_board_t *bp; 309 sbd_io_stat_t *isp; 310 dr_io_unit_t *ip; 311 312 bp = hp->h_bd; 313 314 /* 315 * Only look for requested devices that are actually present. 316 */ 317 devset &= DR_DEVS_PRESENT(bp); 318 319 for (i = ix = 0; i < MAX_IO_UNITS_PER_BOARD; i++) { 320 drmachid_t id; 321 dev_info_t *dip; 322 sbd_error_t *err; 323 drmach_status_t pstat; 324 325 if (DEVSET_IN_SET(devset, SBD_COMP_IO, i) == 0) 326 continue; 327 328 ip = dr_get_io_unit(bp, i); 329 330 if (ip->sbi_cm.sbdev_state == DR_STATE_EMPTY) { 331 /* present, but not fully initialized */ 332 continue; 333 } 334 335 id = ip->sbi_cm.sbdev_id; 336 if (id == (drmachid_t)0) 337 continue; 338 339 err = drmach_status(ip->sbi_cm.sbdev_id, &pstat); 340 if (err) { 341 DRERR_SET_C(&ip->sbi_cm.sbdev_error, &err); 342 return (-1); 343 } 344 345 dip = NULL; 346 err = drmach_get_dip(id, &dip); 347 if (err) { 348 /* catch this in debug kernels */ 349 ASSERT(0); 350 351 sbd_err_clear(&err); 352 continue; 353 } 354 355 isp = &dsp->d_io; 356 bzero((caddr_t)isp, sizeof (*isp)); 357 358 isp->is_cm.c_id.c_type = ip->sbi_cm.sbdev_type; 359 isp->is_cm.c_id.c_unit = ip->sbi_cm.sbdev_unum; 360 strncpy(isp->is_cm.c_id.c_name, pstat.type, 361 sizeof (isp->is_cm.c_id.c_name)); 362 363 dr_get_comp_cond(ip, dip); 364 isp->is_cm.c_cond = ip->sbi_cm.sbdev_cond; 365 isp->is_cm.c_busy = ip->sbi_cm.sbdev_busy | pstat.busy; 366 isp->is_cm.c_time = ip->sbi_cm.sbdev_time; 367 isp->is_cm.c_ostate = ip->sbi_cm.sbdev_ostate; 368 isp->is_cm.c_sflags = 0; 369 370 if (dip == NULL) { 371 isp->is_pathname[0] = '\0'; 372 isp->is_referenced = 0; 373 isp->is_unsafe_count = 0; 374 } else { 375 int refcount = 0, idx = 0; 376 uint64_t unsafe_devs[SBD_MAX_UNSAFE]; 377 378 ASSERT(e_ddi_branch_held(dip)); 379 (void) ddi_pathname(dip, isp->is_pathname); 380 381 /* check reference and unsafe counts on devices */ 382 isp->is_unsafe_count = 0; 383 dr_check_devices(dip, &refcount, hp, unsafe_devs, 384 &idx, SBD_MAX_UNSAFE); 385 while (idx > 0) { 386 isp->is_unsafe_list[idx-1] = unsafe_devs[idx-1]; 387 --idx; 388 } 389 390 isp->is_referenced = (refcount == 0) ? 0 : 1; 391 392 hp->h_err = NULL; 393 } 394 ix++; 395 dsp++; 396 } 397 398 return (ix); 399 } 400