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