xref: /illumos-gate/usr/src/uts/sun4u/ngdr/io/dr_io.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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