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