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