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