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