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 2004 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  * av1394 configuration ROM
31  */
32 #include <sys/file.h>
33 #include <sys/stream.h>
34 #include <sys/strsun.h>
35 #include <sys/1394/targets/av1394/av1394_impl.h>
36 
37 /* configROM parsing */
38 static int	av1394_cfgrom_parse_rom(av1394_inst_t *);
39 static void	av1394_cfgrom_unparse_rom(av1394_inst_t *);
40 static int	av1394_cfgrom_parse_dir(av1394_inst_t *, cmd1394_cmd_t *,
41 		av1394_cfgrom_parse_arg_t *);
42 static void	av1394_cfgrom_add_text_leaf(av1394_inst_t *,
43 		av1394_cfgrom_parsed_dir_t *, uint64_t, uint32_t);
44 static int	av1394_cfgrom_read_leaf(av1394_inst_t *, uint64_t, mblk_t **);
45 static void	av1394_cfgrom_grow_parsed_dir(av1394_cfgrom_parsed_dir_t *,
46 		int);
47 
48 /* routines involving bus transactions */
49 static int	av1394_cfgrom_rq(av1394_inst_t *, cmd1394_cmd_t *,
50 		uint64_t, uint32_t *);
51 
52 /* the following macros emulate throwing an exception when read fails */
53 #define	AV1394_CFGROM_RQ(avp, cmd, addr, valp) \
54 	if ((ret = av1394_cfgrom_rq(avp, cmd, addr, valp)) != 0) { \
55 		goto catch; \
56 	}
57 
58 #define	AV1394_TNF_ENTER(func)	\
59 	TNF_PROBE_0_DEBUG(func##_enter, AV1394_TNF_ASYNC_STACK, "");
60 
61 #define	AV1394_TNF_EXIT(func)	\
62 	TNF_PROBE_0_DEBUG(func##_exit, AV1394_TNF_ASYNC_STACK, "");
63 
64 int
65 av1394_cfgrom_init(av1394_inst_t *avp)
66 {
67 	av1394_cfgrom_t		*crp = &avp->av_a.a_cfgrom;
68 	ddi_iblock_cookie_t	ibc = avp->av_attachinfo.iblock_cookie;
69 
70 	AV1394_TNF_ENTER(av1394_cfgrom_init);
71 
72 	rw_init(&crp->cr_rwlock, NULL, RW_DRIVER, ibc);
73 
74 	AV1394_TNF_EXIT(av1394_cfgrom_init);
75 	return (DDI_SUCCESS);
76 }
77 
78 void
79 av1394_cfgrom_fini(av1394_inst_t *avp)
80 {
81 	av1394_cfgrom_t	*crp = &avp->av_a.a_cfgrom;
82 
83 	AV1394_TNF_ENTER(av1394_cfgrom_fini);
84 
85 	rw_destroy(&crp->cr_rwlock);
86 
87 	AV1394_TNF_EXIT(av1394_cfgrom_fini);
88 }
89 
90 void
91 av1394_cfgrom_close(av1394_inst_t *avp)
92 {
93 	av1394_cfgrom_t	*crp = &avp->av_a.a_cfgrom;
94 
95 	AV1394_TNF_ENTER(av1394_cfgrom_close);
96 
97 	rw_enter(&crp->cr_rwlock, RW_WRITER);
98 	if (crp->cr_parsed) {
99 		av1394_cfgrom_unparse_rom(avp);
100 	}
101 	rw_exit(&crp->cr_rwlock);
102 
103 	AV1394_TNF_EXIT(av1394_cfgrom_close);
104 }
105 
106 int
107 av1394_ioctl_node_get_bus_name(av1394_inst_t *avp, void *arg, int mode)
108 {
109 	cmd1394_cmd_t	*cmd;
110 	uint32_t	val;
111 	int		err;
112 	int		ret = 0;
113 
114 	AV1394_TNF_ENTER(av1394_ioctl_node_get_bus_name);
115 
116 	err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd);
117 	if (err != DDI_SUCCESS) {
118 		AV1394_TNF_EXIT(av1394_ioctl_node_get_bus_name);
119 		return (ENOMEM);
120 	}
121 
122 	ret = av1394_cfgrom_rq(avp, cmd, AV1394_CFGROM_BUS_NAME_ADDR, &val);
123 	if (ret == 0) {
124 		if (ddi_copyout(&val, arg, sizeof (uint32_t), mode) != 0) {
125 			ret = EFAULT;
126 		}
127 	}
128 
129 	err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd);
130 	ASSERT(err == DDI_SUCCESS);
131 
132 	AV1394_TNF_EXIT(av1394_ioctl_node_get_bus_name);
133 	return (ret);
134 }
135 
136 int
137 av1394_ioctl_node_get_uid(av1394_inst_t *avp, void *arg, int mode)
138 {
139 	cmd1394_cmd_t	*cmd;
140 	uint64_t	eui64;
141 	uint32_t	hi, lo;
142 	int		err;
143 	int		ret = 0;
144 
145 	AV1394_TNF_ENTER(av1394_ioctl_node_get_uid);
146 
147 	err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd);
148 	if (err != DDI_SUCCESS) {
149 		AV1394_TNF_EXIT(av1394_ioctl_node_get_uid);
150 		return (ENOMEM);
151 	}
152 
153 	AV1394_CFGROM_RQ(avp, cmd, AV1394_CFGROM_EUI64_HI_ADDR, &hi);
154 	AV1394_CFGROM_RQ(avp, cmd, AV1394_CFGROM_EUI64_LO_ADDR, &lo);
155 
156 	eui64 = ((uint64_t)hi << 32) | lo;
157 	if (ddi_copyout(&eui64, arg, sizeof (uint64_t), mode) != 0) {
158 		ret = EFAULT;
159 	}
160 
161 catch:
162 	err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd);
163 	ASSERT(err == DDI_SUCCESS);
164 
165 	AV1394_TNF_EXIT(av1394_ioctl_node_get_uid);
166 	return (ret);
167 }
168 
169 int
170 av1394_ioctl_node_get_text_leaf(av1394_inst_t *avp, void *arg, int mode)
171 {
172 	av1394_cfgrom_t	*crp = &avp->av_a.a_cfgrom;
173 	iec61883_node_text_leaf_t tl;
174 #ifdef _MULTI_DATAMODEL
175 	iec61883_node_text_leaf32_t tl32;
176 #endif
177 	int		n;		/* text leaf number requested */
178 	int		parent;		/* leaf parent */
179 	mblk_t		*bp = NULL;
180 	av1394_cfgrom_parsed_dir_t *pd;
181 	int		leaf_len;
182 	uint32_t	spec, lang_id, desc_entry;
183 	int		ret = 0;
184 
185 	AV1394_TNF_ENTER(av1394_ioctl_node_get_text_leaf);
186 
187 	/* copyin arguments */
188 #ifdef _MULTI_DATAMODEL
189 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
190 		if (ddi_copyin(arg, &tl32, sizeof (tl32), mode) != 0) {
191 			AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
192 			return (EFAULT);
193 		}
194 		n = tl32.tl_num;
195 		parent = tl32.tl_parent;
196 	} else {
197 #endif
198 	if (ddi_copyin(arg, &tl, sizeof (tl), mode) != 0) {
199 		AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
200 		return (EFAULT);
201 	}
202 	n = tl.tl_num;
203 	parent = tl.tl_parent;
204 #ifdef _MULTI_DATAMODEL
205 	}
206 #endif
207 	/* verify arguments */
208 	if (((parent != IEC61883_ROM_ROOT) && (parent != IEC61883_ROM_UNIT)) ||
209 	    (n < 0)) {
210 		return (EINVAL);
211 	}
212 
213 	/* parse ConfigROM if not already */
214 	rw_enter(&crp->cr_rwlock, RW_WRITER);
215 	if (!crp->cr_parsed) {
216 		ret = av1394_cfgrom_parse_rom(avp);
217 		if (ret != 0) {
218 			rw_exit(&crp->cr_rwlock);
219 			AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
220 			return (ret);
221 		}
222 	}
223 	rw_downgrade(&crp->cr_rwlock);
224 
225 	/* get parsed leaf info */
226 	if (parent == IEC61883_ROM_ROOT) {
227 		pd = &crp->cr_root_dir;
228 	} else {
229 		pd = &crp->cr_unit_dir;
230 	}
231 
232 	if (n < pd->pd_tl_next) {
233 		/* read the leaf */
234 		ret = av1394_cfgrom_read_leaf(avp, pd->pd_tl[n].tl_addr, &bp);
235 		if (ret != 0) {
236 			rw_exit(&crp->cr_rwlock);
237 			AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
238 			return (ret);
239 		}
240 		leaf_len = MBLKL(bp) / 4 - 2;
241 		ASSERT(leaf_len > 0);
242 		spec = *(uint32_t *)bp->b_rptr;
243 		bp->b_rptr += 4;
244 		lang_id = *(uint32_t *)bp->b_rptr;
245 		bp->b_rptr += 4;
246 		desc_entry = pd->pd_tl[n].tl_desc_entry;
247 	} else {
248 		/* return success anyway, but with tl_cnt < tl_num */
249 		spec = lang_id = desc_entry = 0;
250 		leaf_len = 0;
251 	}
252 
253 	/* copyout the results */
254 #ifdef _MULTI_DATAMODEL
255 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
256 		tl32.tl_cnt = pd->pd_tl_next;
257 		tl32.tl_desc_entry = desc_entry;
258 		tl32.tl_rlen = leaf_len;
259 		tl32.tl_spec = spec;
260 		tl32.tl_lang_id = lang_id;
261 		if (ddi_copyout(&tl32, arg, sizeof (tl32), mode) != 0) {
262 			ret = EFAULT;
263 		} else if (bp && ddi_copyout(bp->b_rptr,
264 			    (void *)(uintptr_t)tl32.tl_data,
265 			    4 * min(tl32.tl_len, tl32.tl_rlen), mode) != 0) {
266 			ret = EFAULT;
267 		}
268 	} else {
269 #endif
270 	tl.tl_cnt = pd->pd_tl_next;
271 	tl.tl_desc_entry = desc_entry;
272 	tl.tl_rlen = leaf_len;
273 	tl.tl_spec = spec;
274 	tl.tl_lang_id = lang_id;
275 	if (ddi_copyout(&tl, arg, sizeof (tl), mode) != 0) {
276 		ret = EFAULT;
277 	} else if (bp && ddi_copyout(bp->b_rptr, tl.tl_data,
278 		    4 * min(tl.tl_len, tl.tl_rlen), mode) != 0) {
279 		ret = EFAULT;
280 	}
281 #ifdef _MULTI_DATAMODEL
282 	}
283 #endif
284 	rw_exit(&crp->cr_rwlock);
285 
286 	freemsg(bp);
287 
288 	AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
289 	return (ret);
290 }
291 
292 
293 /*
294  *
295  * --- configROM parsing
296  *
297  * Parse entire configROM. Only extract information that interests us.
298  * ConfigROM integrity checks are only made to ensure correct parsing.
299  */
300 static int
301 av1394_cfgrom_parse_rom(av1394_inst_t *avp)
302 {
303 	av1394_cfgrom_t	*crp = &avp->av_a.a_cfgrom;
304 	cmd1394_cmd_t	*cmd;
305 	uint32_t	val;
306 	uint64_t	root_addr;	/* root dir address */
307 	uint16_t	root_len;	/* root dir length */
308 	av1394_cfgrom_parse_arg_t pa;
309 	int		err;
310 	int		ret;
311 
312 	ASSERT(crp->cr_parsed == B_FALSE);
313 
314 	err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd);
315 	if (err != DDI_SUCCESS) {
316 		return (ENOMEM);
317 	}
318 
319 	/* skip info_len quadlets to get root dir address and length */
320 	AV1394_CFGROM_RQ(avp, cmd, AV1394_CFGROM_INFO_LEN_ADDR, &val);
321 	root_addr = IEEE1394_CONFIG_ROM_ADDR + 4 + (val >> 24) * 4;
322 	AV1394_CFGROM_RQ(avp, cmd, root_addr, &val);
323 	root_len = IEEE1212_DIR_LEN(val);
324 
325 	/* parse root dir and everything underneath */
326 	pa.pa_depth = 0;
327 	pa.pa_desc_entry = 0;
328 	pa.pa_parent_k = 0;
329 	pa.pa_addr = root_addr + 4;
330 	pa.pa_len = root_len;
331 	pa.pa_dir = &crp->cr_root_dir;
332 
333 	ret = av1394_cfgrom_parse_dir(avp, cmd, &pa);
334 
335 catch:
336 	if (ret == 0) {
337 		crp->cr_parsed = B_TRUE;
338 	} else {
339 		av1394_cfgrom_unparse_rom(avp);
340 	}
341 	err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd);
342 	ASSERT(err == DDI_SUCCESS);
343 
344 	return (ret);
345 }
346 
347 /*
348  * parse a directory
349  */
350 static int
351 av1394_cfgrom_parse_dir(av1394_inst_t *avp, cmd1394_cmd_t *cmd,
352 		av1394_cfgrom_parse_arg_t *pa)
353 {
354 	av1394_cfgrom_t	*crp = &avp->av_a.a_cfgrom;
355 	int		i;
356 	uint64_t	entry_addr;
357 	uint32_t	entry;
358 	uint64_t	leaf_addr;
359 	uint64_t	dir_addr;
360 	uint16_t	dir_len;
361 	uint8_t		t, k;
362 	uint16_t	v;
363 	uint32_t	val;
364 	av1394_cfgrom_parse_arg_t this_pa;
365 	int		ret = 0;
366 
367 	/* safeguard against deep recursion */
368 	if (pa->pa_depth > AV1394_CFGROM_PARSE_MAX_DEPTH) {
369 		return (ENOMEM);
370 	}
371 
372 	/* initialize parse arguments */
373 	this_pa.pa_depth = pa->pa_depth + 1;
374 	this_pa.pa_desc_entry = pa->pa_desc_entry;
375 
376 	/* walk dir entries */
377 	entry_addr = pa->pa_addr;
378 	for (i = 0; i < pa->pa_len; i++) {
379 		AV1394_CFGROM_RQ(avp, cmd, entry_addr, &entry);
380 
381 		CFGROM_TYPE_KEY_VALUE(entry, t, k, v);
382 		if ((t == IEEE1212_LEAF_TYPE) &&
383 		    (k == IEEE1212_TEXTUAL_DESCRIPTOR)) {
384 			/* save this leaf */
385 			leaf_addr = entry_addr + 4 * v;
386 			av1394_cfgrom_add_text_leaf(avp, pa->pa_dir,
387 					leaf_addr, this_pa.pa_desc_entry);
388 		} else if (t == IEEE1212_DIRECTORY_TYPE) {
389 			dir_addr = entry_addr + 4 * v;
390 			AV1394_CFGROM_RQ(avp, cmd, dir_addr, &val);
391 			dir_len = IEEE1212_DIR_LEN(val);
392 
393 			/* parse this dir */
394 			this_pa.pa_parent_k = k;
395 			this_pa.pa_addr = dir_addr + 4;
396 			this_pa.pa_len = dir_len;
397 			/* leaves will be added to either root or unit array */
398 			if (k == IEEE1212_UNIT_DIRECTORY) {
399 				this_pa.pa_dir = &crp->cr_unit_dir;
400 			} else {
401 				this_pa.pa_dir = pa->pa_dir;
402 			}
403 
404 			ret = av1394_cfgrom_parse_dir(avp, cmd, &this_pa);
405 			if (ret != 0) {
406 				goto catch;
407 			}
408 		}
409 
410 		/*
411 		 * if we're walking Textual_Descriptor directory,
412 		 * the described entry is the one preceding directory's entry,
413 		 * so we need to preserve what was passed in pa->pa_desc_entry
414 		 */
415 		if (pa->pa_parent_k != IEEE1212_TEXTUAL_DESCRIPTOR) {
416 			this_pa.pa_desc_entry = entry;
417 		}
418 		entry_addr += 4;
419 	}
420 
421 catch:
422 	return (ret);
423 }
424 
425 /*ARGSUSED*/
426 static void
427 av1394_cfgrom_add_text_leaf(av1394_inst_t *avp, av1394_cfgrom_parsed_dir_t *pd,
428 		uint64_t addr, uint32_t desc_entry)
429 {
430 	/* grow array of needed */
431 	if (pd->pd_tl_next >= pd->pd_tl_size) {
432 		av1394_cfgrom_grow_parsed_dir(pd, 2);
433 	}
434 	pd->pd_tl[pd->pd_tl_next].tl_addr = addr;
435 	pd->pd_tl[pd->pd_tl_next].tl_desc_entry = desc_entry;
436 	pd->pd_tl_next++;
437 }
438 
439 /*
440  * this routine cleans up after av1394_cfgrom_parse()
441  */
442 static void
443 av1394_cfgrom_unparse_rom(av1394_inst_t *avp)
444 {
445 	av1394_cfgrom_t	*crp = &avp->av_a.a_cfgrom;
446 	av1394_cfgrom_parsed_dir_t *pd;
447 
448 	pd = &crp->cr_root_dir;
449 	if (pd->pd_tl) {
450 		kmem_free(pd->pd_tl, pd->pd_tl_size * sizeof (*pd->pd_tl));
451 		bzero(pd, sizeof (*pd));
452 	}
453 	pd = &crp->cr_unit_dir;
454 	if (pd->pd_tl) {
455 		kmem_free(pd->pd_tl, pd->pd_tl_size * sizeof (*pd->pd_tl));
456 		bzero(pd, sizeof (*pd));
457 	}
458 	crp->cr_parsed = B_FALSE;
459 }
460 
461 /*
462  * grow parsed dir leaf array by 'cnt' entries
463  */
464 static void
465 av1394_cfgrom_grow_parsed_dir(av1394_cfgrom_parsed_dir_t *pd, int cnt)
466 {
467 	int	new_size;
468 	void	*new_tl;
469 
470 	ASSERT(cnt > 0);
471 
472 	new_size = (pd->pd_tl_size + cnt) * sizeof (av1394_cfgrom_text_leaf_t);
473 	new_tl = kmem_zalloc(new_size, KM_SLEEP);
474 	if (pd->pd_tl_size > 0) {
475 		bcopy(pd->pd_tl, new_tl, pd->pd_tl_size * sizeof (*pd->pd_tl));
476 		kmem_free(pd->pd_tl, pd->pd_tl_size * sizeof (*pd->pd_tl));
477 	}
478 	pd->pd_tl = new_tl;
479 	pd->pd_tl_size += cnt;
480 }
481 
482 static int
483 av1394_cfgrom_read_leaf(av1394_inst_t *avp, uint64_t leaf_addr, mblk_t **bpp)
484 {
485 	cmd1394_cmd_t	*cmd;
486 	uint64_t	addr;
487 	uint32_t	val;
488 	int		leaf_len;	/* leaf length in quadlets */
489 	mblk_t		*bp = NULL;
490 	int		i;
491 	int		err;
492 	int		ret = 0;
493 
494 	err = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd);
495 	if (err != DDI_SUCCESS) {
496 		AV1394_TNF_EXIT(av1394_ioctl_node_get_text_leaf);
497 		return (ENOMEM);
498 	}
499 
500 	/* read leaf length */
501 	AV1394_CFGROM_RQ(avp, cmd, leaf_addr, &val);
502 	leaf_len = IEEE1212_DIR_LEN(val);
503 
504 	if (leaf_len < 3) {
505 		ret = EIO;
506 		goto catch;
507 	}
508 
509 	if ((bp = allocb(leaf_len * 4, BPRI_HI)) == NULL) {
510 		TNF_PROBE_0(aav1394_cfgrom_read_leaf_error_allocb,
511 		    AV1394_TNF_ASYNC_ERROR, "");
512 		return (ENOMEM);
513 	}
514 
515 	/* read leaf value */
516 	addr = leaf_addr + 4;
517 	for (i = 0; i < leaf_len; i++) {
518 		AV1394_CFGROM_RQ(avp, cmd, addr, (uint32_t *)bp->b_wptr);
519 		bp->b_wptr += 4;
520 		addr += 4;
521 	}
522 
523 catch:
524 	if (ret == 0) {
525 		*bpp = bp;
526 	} else {
527 		freemsg(bp);
528 	}
529 	err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd);
530 	ASSERT(err == DDI_SUCCESS);
531 
532 	return (ret);
533 }
534 
535 /*
536  *
537  * --- routines involving bus transactions
538  *
539  */
540 static int
541 av1394_cfgrom_rq(av1394_inst_t *avp, cmd1394_cmd_t *cmd, uint64_t addr,
542 		uint32_t *rval)
543 {
544 	int	err;
545 
546 	cmd->cmd_type = CMD1394_ASYNCH_RD_QUAD;
547 	cmd->cmd_options = CMD1394_BLOCKING;
548 	cmd->cmd_addr = addr;
549 
550 	err = t1394_read(avp->av_t1394_hdl, cmd);
551 	if ((err == DDI_SUCCESS) && (cmd->cmd_result == CMD1394_CMDSUCCESS)) {
552 		*rval = cmd->cmd_u.q.quadlet_data;
553 		return (0);
554 	} else {
555 		TNF_PROBE_2(av1394_cfgrom_rq_error,
556 		    AV1394_TNF_ASYNC_ERROR, "", tnf_int, err, err,
557 		    tnf_int, result, cmd->cmd_result);
558 		return (EIO);
559 	}
560 }
561