xref: /illumos-gate/usr/src/uts/sun4/io/efcode/fc_ops.c (revision f6e214c7418f43af38bd8c3a557e3d0a1d311cfa)
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 2000, 2002 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  * fc_ops.c: Framework generic fcode ops
31  */
32 #include <sys/types.h>
33 #include <sys/kmem.h>
34 #include <sys/systm.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 #include <sys/sunndi.h>
38 #include <sys/modctl.h>
39 #include <sys/fcode.h>
40 #include <sys/ddi_implfuncs.h>
41 #include <sys/ndi_impldefs.h>
42 #include <sys/ethernet.h>
43 
44 static int fco_new_device(dev_info_t *, fco_handle_t, fc_ci_t *);
45 static int fco_finish_device(dev_info_t *, fco_handle_t, fc_ci_t *);
46 static int fco_create_property(dev_info_t *, fco_handle_t, fc_ci_t *);
47 
48 static int fco_validate(dev_info_t *, fco_handle_t, fc_ci_t *);
49 static int fco_invalidate(dev_info_t *, fco_handle_t, fc_ci_t *);
50 static int fco_exit(dev_info_t *, fco_handle_t, fc_ci_t *);
51 
52 static int fco_getproplen(dev_info_t *, fco_handle_t, fc_ci_t *);
53 static int fco_getprop(dev_info_t *, fco_handle_t, fc_ci_t *);
54 
55 static int fco_ap_phandle(dev_info_t *, fco_handle_t, fc_ci_t *);
56 static int fco_child(dev_info_t *, fco_handle_t, fc_ci_t *);
57 static int fco_peer(dev_info_t *, fco_handle_t, fc_ci_t *);
58 static int fco_parent(dev_info_t *, fco_handle_t, fc_ci_t *);
59 static int fco_alloc_phandle(dev_info_t *, fco_handle_t, fc_ci_t *);
60 
61 static int fco_local_ether_addr(dev_info_t *, fco_handle_t, fc_ci_t *);
62 
63 struct fc_ops_v {
64 	char *svc_name;
65 	fc_ops_t *f;
66 };
67 
68 static struct fc_ops_v fov[] = {
69 	{	"open",			fc_fail_op},
70 	{	"close",		fc_fail_op},
71 	{	"$find",		fc_fail_op},
72 	{	"encode-unit",		fc_fail_op},
73 	{	"decode-unit",		fc_fail_op},
74 	{	FC_GET_MY_PROPLEN,	fco_getproplen},
75 	{	FC_GET_MY_PROP,		fco_getprop},
76 	{	FC_GET_PKG_PROPLEN,	fco_getproplen},
77 	{	FC_GET_PKG_PROP,	fco_getprop},
78 	{	FC_GET_IN_PROPLEN,	fco_getproplen},
79 	{	FC_GET_IN_PROP,		fco_getprop},
80 	{	FC_NEW_DEVICE,		fco_new_device},
81 	{	FC_FINISH_DEVICE,	fco_finish_device},
82 	{	FC_CREATE_PROPERTY,	fco_create_property},
83 	{	FC_AP_PHANDLE,		fco_ap_phandle},
84 	{	"child",		fco_child},
85 	{	"peer",			fco_peer},
86 	{	FC_PARENT,		fco_parent},
87 	{	FC_ALLOC_PHANDLE,	fco_alloc_phandle},
88 	{	FC_SVC_VALIDATE,	fco_validate},
89 	{	FC_SVC_INVALIDATE,	fco_invalidate},
90 	{	FC_SVC_EXIT,		fco_exit},
91 	{	"local-ether-addr",	fco_local_ether_addr},
92 	{	NULL,			NULL}
93 };
94 
95 /*
96  * Allocate a handle for the ops function .. our handle is a resource list
97  * Return the handle to our caller, so he can call us with it when we need it.
98  */
99 /*ARGSUSED*/
100 fco_handle_t
101 fc_ops_alloc_handle(dev_info_t *ap, dev_info_t *child,
102     void *fcode, size_t fcode_size, char *unit_address, void *bus_args)
103 {
104 	fco_handle_t rp;
105 	char *up;
106 
107 	rp = kmem_zalloc(sizeof (struct fc_resource_list), KM_SLEEP);
108 	rp->next_handle = NULL;		/* nobody is downstream */
109 	rp->ap = ap;
110 	rp->child = child;
111 	rp->fcode = fcode;
112 	rp->fcode_size = fcode_size;
113 	if (unit_address) {
114 		up = kmem_zalloc(strlen(unit_address) + 1, KM_SLEEP);
115 		(void) strcpy(up, unit_address);
116 		rp->unit_address = up;
117 	}
118 	rp->bus_args = NULL;		/* generic module has no bus args */
119 	fc_phandle_table_alloc(fc_handle_to_phandle_head(rp));
120 
121 	(void) fc_dip_to_phandle(fc_handle_to_phandle_head(rp), ap);
122 
123 	/*
124 	 * Create our copy of the device tree.
125 	 */
126 	fc_create_device_tree(ap, &rp->dtree);
127 	return (rp);
128 }
129 
130 /*
131  * Free any resources associated with this handle.
132  */
133 void
134 fc_ops_free_handle(fco_handle_t rp)
135 {
136 	struct fc_resource *ip, *np;
137 
138 	if (rp->unit_address)
139 		kmem_free(rp->unit_address, strlen(rp->unit_address) + 1);
140 
141 	if (rp->dtree)
142 		fc_remove_device_tree(&rp->dtree);
143 
144 	fc_phandle_table_free(fc_handle_to_phandle_head(rp));
145 
146 	for (ip = rp->head; ip != NULL; ip = np) {
147 		np = ip->next;
148 		switch (ip->type) {
149 		case RT_NODEID:
150 			impl_ddi_free_nodeid(ip->fc_nodeid_r);
151 			break;
152 		default:
153 			cmn_err(CE_CONT, "pci_fc_ops_free: "
154 			    "unknown resource type %d\n", ip->type);
155 			break;
156 		}
157 		fc_rem_resource(rp, ip);
158 		kmem_free(ip, sizeof (struct fc_resource));
159 	}
160 	kmem_free(rp, sizeof (struct fc_resource_list));
161 }
162 
163 int
164 fc_ops(dev_info_t *ap, fco_handle_t handle, fc_ci_t *cp)
165 {
166 	struct fc_ops_v *pv;
167 	char *name = fc_cell2ptr(cp->svc_name);
168 
169 	for (pv = fov; pv->svc_name != NULL; ++pv)
170 		if (strcmp(pv->svc_name, name) == 0)
171 			return (pv->f(ap, handle, cp));
172 
173 	return (-1);
174 }
175 
176 /*
177  * The interpreter can't do get-inherited-property directly,
178  * because we don't want to return a kernel address, so it
179  * has to break up the request into a get-proplen and get-prop
180  * call so it can allocate memory for the property and pass that
181  * buffer in to get-prop.  The buffer should be 'suitably aligned'.
182  *
183  * XXX: We don't know the property type, so we can't return
184  * prop-encoded arrays, which fortunately, isn't a problem
185  * on big-endian machines.
186  *
187  * get-proplen has one result: proplen
188  * proplen is returned as -1 if the propname doesn't exist and
189  * as zero if the property is a boolean property.
190  *
191  * get-prop has one result: proplen, returned as -1 if propname doesn't exist.
192  */
193 
194 /*
195  * fco_getproplen ( propname phandle -- proplen )
196  */
197 
198 /*ARGSUSED*/
199 static int
200 fco_getproplen(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
201 {
202 	int proplen;
203 	int flags = 0;
204 	fc_phandle_t h;
205 	dev_info_t *dip;
206 	char *pnp;
207 	char propname[OBP_MAXPROPNAME];
208 
209 	if (strstr(fc_cell2ptr(cp->svc_name), "inherited") == NULL)
210 		flags |= DDI_PROP_DONTPASS;
211 
212 	if (fc_cell2int(cp->nargs) != 2)
213 		return (fc_syntax_error(cp, "nargs must be 2"));
214 
215 	if (fc_cell2int(cp->nresults) < 1)
216 		return (fc_syntax_error(cp, "nresults must be > 0"));
217 
218 	/*
219 	 * Make sure this is a handle we gave out ...
220 	 */
221 	h = fc_cell2phandle(fc_arg(cp, 0));
222 	if ((dip = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), h)) == NULL)
223 		return (fc_priv_error(cp, "unknown handle"));
224 
225 	/*
226 	 * XXX: We should care if the string is longer than OBP_MAXPROPNAME
227 	 */
228 	pnp = fc_cell2ptr(fc_arg(cp, 1));
229 	bzero(propname, OBP_MAXPROPNAME);
230 	if (copyinstr(pnp, propname, OBP_MAXPROPNAME - 1, NULL))
231 		return (fc_priv_error(cp, "EFAULT copying in propname"));
232 
233 	if (ddi_getproplen(DDI_DEV_T_ANY, dip, flags, propname, &proplen))
234 		proplen = -1;
235 
236 	fc_result(cp, 0) = fc_int2cell(proplen);
237 	cp->nresults = fc_int2cell(1);
238 	return (fc_success_op(ap, rp, cp));
239 }
240 
241 /*
242  * fco_getprop ( propname buffer phandle -- proplen )
243  */
244 
245 /*ARGSUSED*/
246 static int
247 fco_getprop(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
248 {
249 	int proplen = -1;
250 	int flags = DDI_PROP_CANSLEEP;
251 	char *pnp, *bp;
252 	fc_phandle_t h;
253 	dev_info_t *dip;
254 	char propname[OBP_MAXPROPNAME];
255 
256 	if (strstr(fc_cell2ptr(cp->svc_name), "inherited") == NULL)
257 		flags |= DDI_PROP_DONTPASS;
258 
259 	if (fc_cell2int(cp->nargs) != 3)
260 		return (fc_syntax_error(cp, "nargs must be 3"));
261 
262 	if (fc_cell2int(cp->nresults) < 1)
263 		return (fc_syntax_error(cp, "nresults must be > 0"));
264 
265 	/*
266 	 * Make sure this is a handle we gave out ...
267 	 */
268 	h = fc_cell2phandle(fc_arg(cp, 0));
269 	if ((dip = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), h)) == NULL)
270 		return (fc_priv_error(cp, "unknown handle"));
271 
272 	/*
273 	 * XXX: We should care if the string is longer than OBP_MAXPROPNAME
274 	 */
275 	pnp = fc_cell2ptr(fc_arg(cp, 2));
276 	bzero(propname, OBP_MAXPROPNAME);
277 	if (copyinstr(pnp, propname, OBP_MAXPROPNAME - 1, NULL))
278 		return (fc_priv_error(cp, "EFAULT copying in propname"));
279 
280 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, flags,
281 	    propname, (caddr_t)&bp, &proplen))
282 		proplen = -1;
283 
284 	if (proplen > 0) {
285 		char *up = fc_cell2ptr(fc_arg(cp, 1));
286 		int error;
287 
288 		error = copyout(bp, up, proplen);
289 		kmem_free(bp, proplen);
290 		if (error)
291 			return (fc_priv_error(cp, "EFAULT copying data out"));
292 	}
293 
294 	cp->nresults = fc_int2cell(1);
295 	fc_result(cp, 0) = fc_int2cell(proplen);
296 	return (fc_success_op(ap, rp, cp));
297 }
298 
299 static int
300 fco_ap_phandle(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
301 {
302 	fc_phandle_t h;
303 
304 	if (fc_cell2int(cp->nargs) != 0)
305 		return (fc_syntax_error(cp, "nargs must be 0"));
306 
307 	if (fc_cell2int(cp->nresults) < 1)
308 		return (fc_syntax_error(cp, "nresults must be > 0"));
309 
310 	FC_DEBUG1(9, CE_CONT, "fco_ap_phandle: Looking up ap dip %p\n", ap);
311 
312 	h = fc_dip_to_phandle(fc_handle_to_phandle_head(rp), ap);
313 	cp->nresults = fc_int2cell(1);
314 	fc_result(cp, 0) = fc_phandle2cell(h);
315 	return (fc_success_op(ap, rp, cp));
316 }
317 
318 static int
319 fco_child(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
320 {
321 	fc_phandle_t h;
322 	dev_info_t *dip;
323 
324 	if (fc_cell2int(cp->nargs) != 1)
325 		return (fc_syntax_error(cp, "nargs must be 1"));
326 
327 	if (fc_cell2int(cp->nresults) < 1)
328 		return (fc_syntax_error(cp, "nresults must be > 0"));
329 
330 	/*
331 	 * Make sure this is a handle we gave out ...
332 	 */
333 	h = fc_cell2phandle(fc_arg(cp, 0));
334 	if ((dip = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), h)) == NULL)
335 		return (fc_priv_error(cp, "unknown handle"));
336 
337 	/*
338 	 * Find the child and if there is one, return it ...
339 	 */
340 	dip = ddi_get_child(dip);
341 	h = 0;
342 	if (dip != NULL)
343 		h = fc_dip_to_phandle(fc_handle_to_phandle_head(rp), dip);
344 
345 	cp->nresults = fc_int2cell(1);
346 	fc_result(cp, 0) = fc_phandle2cell(h);
347 	return (fc_success_op(ap, rp, cp));
348 }
349 
350 static int
351 fco_peer(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
352 {
353 	fc_phandle_t h;
354 	dev_info_t *dip;
355 
356 	if (fc_cell2int(cp->nargs) != 1)
357 		return (fc_syntax_error(cp, "nargs must be 1"));
358 
359 	if (fc_cell2int(cp->nresults) < 1)
360 		return (fc_syntax_error(cp, "nresults must be > 0"));
361 
362 	/*
363 	 * Make sure this is a handle we gave out ...
364 	 */
365 	h = fc_cell2phandle(fc_arg(cp, 0));
366 	if ((dip = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), h)) == NULL)
367 		return (fc_priv_error(cp, "unknown handle"));
368 
369 	/*
370 	 * Find the child and if there is one, return it ...
371 	 */
372 	dip = ddi_get_next_sibling(dip);
373 	h = 0;
374 	if (dip != NULL)
375 		h = fc_dip_to_phandle(fc_handle_to_phandle_head(rp), dip);
376 
377 	cp->nresults = fc_int2cell(1);
378 	fc_result(cp, 0) = fc_phandle2cell(h);
379 	return (fc_success_op(ap, rp, cp));
380 }
381 
382 static int
383 fco_parent(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
384 {
385 	fc_phandle_t h;
386 	dev_info_t *dip;
387 
388 	if (fc_cell2int(cp->nargs) != 1)
389 		return (fc_syntax_error(cp, "nargs must be 1"));
390 
391 	if (fc_cell2int(cp->nresults) < 1)
392 		return (fc_syntax_error(cp, "nresults must be > 0"));
393 
394 	/*
395 	 * Make sure this is a handle we gave out ...
396 	 */
397 	h = fc_cell2phandle(fc_arg(cp, 0));
398 	if ((dip = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), h)) == NULL)
399 		return (fc_priv_error(cp, "unknown handle"));
400 
401 	/*
402 	 * Find the parent and if there is one, return it ...
403 	 */
404 	dip = ddi_get_parent(dip);
405 	h = 0;
406 	if (dip != NULL)
407 		h = fc_dip_to_phandle(fc_handle_to_phandle_head(rp), dip);
408 
409 	cp->nresults = fc_int2cell(1);
410 	fc_result(cp, 0) = fc_phandle2cell(h);
411 	return (fc_success_op(ap, rp, cp));
412 }
413 
414 /*
415  * Allocate a phandle ... we don't currently track the phandle.
416  */
417 static int
418 fco_alloc_phandle(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
419 {
420 	fc_phandle_t h;
421 	int n;
422 	struct fc_resource *ip;
423 
424 	if (fc_cell2int(cp->nargs) != 0)
425 		return (fc_syntax_error(cp, "nargs must be 0"));
426 
427 	if (fc_cell2int(cp->nresults) < 1)
428 		return (fc_syntax_error(cp, "nresults must be > 0"));
429 
430 	if (impl_ddi_alloc_nodeid(&n))
431 		return (fc_priv_error(cp, "Can't allocate a nodeid"));
432 
433 	/*
434 	 * Log the nodeid resource so we can release it later if we need to.
435 	 */
436 	ip = kmem_zalloc(sizeof (struct fc_resource), KM_SLEEP);
437 	ip->type = RT_NODEID;
438 	ip->fc_nodeid_r = n;
439 	fc_add_resource(rp, ip);
440 
441 	h = (fc_phandle_t)n;
442 
443 	cp->nresults = fc_int2cell(1);
444 	fc_result(cp, 0) = fc_phandle2cell(h);
445 	return (fc_success_op(ap, rp, cp));
446 }
447 
448 static struct fc_resource *
449 find_nodeid_resource(fco_handle_t rp, int n)
450 {
451 	struct fc_resource *ip;
452 
453 	fc_lock_resource_list(rp);
454 	for (ip = rp->head; ip != NULL; ip = ip->next) {
455 		if (ip->type != RT_NODEID)
456 			continue;
457 		if (ip->fc_nodeid_r == n)
458 			break;
459 	}
460 	fc_unlock_resource_list(rp);
461 
462 	return (ip);
463 }
464 
465 /*
466  * fco_new_device ( name-cstr unit-addr-cstr parent.phandle phandle -- )
467  */
468 static int
469 fco_new_device(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
470 {
471 	fc_phandle_t ph, ch;
472 	dev_info_t *pdev, *cdev;
473 	char *s;
474 	int createmode = 0;
475 	char *unit_address = NULL;
476 	char nodename[OBP_MAXPROPNAME];
477 
478 	if (fc_cell2int(cp->nargs) != 4)
479 		return (fc_syntax_error(cp, "nargs must be 4"));
480 
481 	/*
482 	 * Make sure these are handles we gave out ... and we have
483 	 * a corresponding parent devinfo node.
484 	 */
485 	ph = fc_cell2phandle(fc_arg(cp, 1));
486 	pdev = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), ph);
487 	if (pdev == NULL)
488 		return (fc_priv_error(cp, "unknown parent phandle"));
489 
490 	ch = fc_cell2phandle(fc_arg(cp, 0));
491 	cdev = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), ch);
492 
493 	switch (rp->cdip_state) {
494 
495 	case FC_CDIP_NOSTATE:
496 		/*
497 		 * The first child must be a child of the attachment point.
498 		 */
499 		if (pdev != ap)
500 			return (fc_priv_error(cp, "first child must be a "
501 			    "child of the attachment point"));
502 
503 		/*
504 		 * If this bus has a config child, the first child must
505 		 * be the configuration child. Otherwise, the child must
506 		 * be a new (unknown) node.
507 		 */
508 		if (cdev != NULL) {
509 			if (rp->child != NULL) {
510 				if (cdev != rp->child)
511 					return (fc_priv_error(cp, "first "
512 					    "child must be the "
513 					    "configuration child"));
514 			} else {
515 				return (fc_priv_error(cp, "known child -- "
516 				    "unknown child expected"));
517 			}
518 		}
519 		break;
520 
521 	case FC_CDIP_DONE:
522 		/*
523 		 * If we've already created the first child, this
524 		 * child must be unknown and the parent must be a known
525 		 * child of the attachment point.
526 		 */
527 		if (cdev)
528 			return (fc_priv_error(cp, "known child -- "
529 			    "unknown child expected"));
530 		if (fc_find_node(pdev, fc_handle_to_dtree(rp)) == NULL)
531 			return (fc_priv_error(cp, "parent is an unknown "
532 			    "child of the attachment point"));
533 		break;
534 
535 	default:
536 		/*
537 		 * If we're in some other state, we shouldn't be here.
538 		 */
539 		return (fc_priv_error(cp, "bad node-creation state"));
540 		/* NOTREACHED */
541 	}
542 
543 	/*
544 	 * Get the nodename and the unit address.
545 	 */
546 	s = fc_cell2ptr(fc_arg(cp, 3));
547 	bzero(nodename, OBP_MAXPROPNAME);
548 	if (copyinstr(s, nodename, OBP_MAXPROPNAME - 1, NULL))
549 		return (fc_priv_error(cp, "EFAULT copying in nodename"));
550 
551 	s = fc_cell2ptr(fc_arg(cp, 2));
552 	unit_address = kmem_zalloc(OBP_MAXPATHLEN, KM_SLEEP);
553 	if (copyinstr(s, unit_address, OBP_MAXPATHLEN - 1, NULL)) {
554 		kmem_free(unit_address, OBP_MAXPATHLEN);
555 		return (fc_priv_error(cp, "EFAULT copying in unit address"));
556 	}
557 
558 	/*
559 	 * If cdev is NULL, we have to create the child, otherwise, the
560 	 * child already exists and we're just merging properties into
561 	 * the existing node.  The node must be unbound.
562 	 */
563 
564 	if (cdev == NULL)
565 		createmode = 1;
566 
567 	if (createmode) {
568 		struct fc_resource *ip;
569 		int nodeid;
570 		/*
571 		 * Make sure 'ch' is a nodeid we gave the interpreter.
572 		 * It must be on our resource list.
573 		 */
574 		if ((ip = find_nodeid_resource(rp, (int)ch)) == NULL) {
575 			kmem_free(unit_address, OBP_MAXPATHLEN);
576 			return (fc_priv_error(cp, "Unknown phandle"));
577 		}
578 
579 		/*
580 		 * Allocate a self-identifying, persistent node with
581 		 * the auto-free attribute.
582 		 */
583 		if (ndi_devi_alloc(pdev, nodename, DEVI_SID_NODEID, &cdev)) {
584 			kmem_free(unit_address, OBP_MAXPATHLEN);
585 			return (fc_priv_error(cp, "Can't create node"));
586 		}
587 
588 		/*
589 		 * Free the nodeid we just allocated here, and use
590 		 * the one we handed in. Retain the attributes of
591 		 * the original SID nodetype.
592 		 */
593 		nodeid = ddi_get_nodeid(cdev);
594 		i_ndi_set_nodeid(cdev, (int)ch);
595 		impl_ddi_free_nodeid(nodeid);
596 
597 		/*
598 		 * Remove nodeid 'ch' from our resource list, now that it
599 		 * will be managed by the ddi framework.
600 		 */
601 		fc_rem_resource(rp, ip);
602 		kmem_free(ip, sizeof (struct fc_resource));
603 
604 	} else if (strcmp(ddi_node_name(cdev), nodename) != 0) {
605 		FC_DEBUG2(1, CE_CONT, "Changing <%s> nodename to <%s>\n",
606 		    ddi_node_name(cdev), nodename);
607 		if (ndi_devi_set_nodename(cdev, nodename, 0)) {
608 			kmem_free(unit_address, OBP_MAXPATHLEN);
609 			return (fc_priv_error(cp, "Can't set ndi nodename"));
610 		}
611 	}
612 
613 	if (fc_ndi_prop_update(DDI_DEV_T_NONE, cdev, "name",
614 	    (uchar_t *)nodename, strlen(nodename) + 1)) {
615 		kmem_free(unit_address, OBP_MAXPATHLEN);
616 		if (createmode)
617 			(void) ndi_devi_free(cdev);
618 		return (fc_priv_error(cp, "Can't create name property"));
619 	}
620 
621 	/*
622 	 * Add the dip->phandle translation to our list of known phandles.
623 	 */
624 	fc_add_dip_to_phandle(fc_handle_to_phandle_head(rp), cdev, ch);
625 
626 	/*
627 	 * Add the new node to our copy of the subtree.
628 	 */
629 	fc_add_child(cdev, pdev, fc_handle_to_dtree(rp));
630 
631 	rp->cdip = cdev;
632 	rp->cdip_state = FC_CDIP_STARTED;
633 
634 	kmem_free(unit_address, OBP_MAXPATHLEN);
635 	cp->nresults = fc_int2cell(0);
636 	return (fc_success_op(ap, rp, cp));
637 }
638 
639 /*
640  * fco_finish_device ( phandle -- )
641  */
642 static int
643 fco_finish_device(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
644 {
645 	fc_phandle_t h;
646 	dev_info_t *cdev;
647 
648 	if (fc_cell2int(cp->nargs) != 1)
649 		return (fc_syntax_error(cp, "nargs must be 1"));
650 
651 	if (rp->cdip_state != FC_CDIP_STARTED)
652 		return (fc_priv_error(cp, "bad node-creation state"));
653 
654 	h = fc_cell2phandle(fc_arg(cp, 0));
655 	cdev = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), h);
656 	if (cdev != rp->cdip)
657 		return (fc_priv_error(cp, "bad phandle"));
658 
659 	/*
660 	 * We don't want to online children of the attachment point.
661 	 * We'll 'config' them online later.
662 	 *
663 	 * XXX - APA - I've changed this a bit.  The only time we don't
664 	 * want to bind the device is if the parent is the attachment point
665 	 * and the device is the same as the device that was passed to
666 	 * the interpreter.  We assume the configurator will do the binding.
667 	 */
668 	if ((ddi_get_parent(cdev) == ap) && (cdev == rp->child)) {
669 		FC_DEBUG2(5, CE_CONT, "fc_finish_device: "
670 		    "*not* binding <%s> dip %p\n", ddi_node_name(cdev), cdev);
671 	} else {
672 		FC_DEBUG2(5, CE_CONT, "fc_finish_device: binding <%s> dip %p\n",
673 		    ddi_node_name(cdev), cdev);
674 
675 		(void) ndi_devi_bind_driver(cdev, 0);
676 	}
677 
678 	rp->cdip_state = FC_CDIP_DONE;
679 	cp->nresults = fc_int2cell(0);
680 	return (fc_success_op(ap, rp, cp));
681 }
682 
683 /*
684  * fco_create_property ( propname-cstr buf len phandle -- )
685  */
686 static int
687 fco_create_property(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
688 {
689 	char *buf, *bp, *pnp;
690 	size_t len;
691 	fc_phandle_t h;
692 	dev_info_t *dev;
693 	int error;
694 	char propname[OBP_MAXPROPNAME];
695 
696 	if (fc_cell2int(cp->nargs) != 4)
697 		return (fc_syntax_error(cp, "nargs must be 4"));
698 
699 	h = fc_cell2phandle(fc_arg(cp, 0));
700 	len = fc_cell2size(fc_arg(cp, 1));
701 	bp = fc_cell2ptr(fc_arg(cp, 2));
702 	pnp = fc_cell2ptr(fc_arg(cp, 3));
703 
704 	dev = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), h);
705 	if (dev == NULL)
706 		return (fc_priv_error(cp, "bad phandle"));
707 
708 	bzero(propname, OBP_MAXPROPNAME);
709 	if (copyinstr(pnp, propname, OBP_MAXPROPNAME - 1, NULL))
710 		return (fc_priv_error(cp, "EFAULT copying in propname"));
711 
712 	buf = NULL;
713 	if (len != 0) {
714 		buf = kmem_zalloc(len, KM_SLEEP);
715 		if (copyin(bp, buf, len)) {
716 			kmem_free(buf, len);
717 			return (fc_priv_error(cp, "EFAULT copying in propval"));
718 		}
719 	}
720 
721 	/*
722 	 * check for propname: 'name' ... we don't allow it
723 	 * by changed here.  It has to be specified when the node
724 	 * is created.
725 	 */
726 	if (strcmp(propname, "name") == 0) {
727 		char *n = ddi_node_name(dev);
728 
729 		if (len == 0)
730 			return (fc_priv_error(cp, "setting <name> to NULL"));
731 		if ((len < (strlen(n) + 1)) || (strcmp(n, buf) != 0)) {
732 			kmem_free(buf, len);
733 			return (fc_priv_error(cp, "changing <name> property"));
734 		}
735 		/*
736 		 * Since we're not changing the value, and we already created
737 		 * the 'name' property when we created the node ...
738 		 */
739 		kmem_free(buf, len);
740 		cp->nresults = fc_int2cell(0);
741 		return (fc_success_op(ap, rp, cp));
742 	}
743 
744 	error = fc_ndi_prop_update(DDI_DEV_T_NONE, dev, propname,
745 	    (uchar_t *)buf, len);
746 
747 	if (len != 0)
748 		kmem_free(buf, len);
749 
750 	if (error)
751 		return (fc_priv_error(cp, "Can't create property"));
752 
753 	cp->nresults = fc_int2cell(0);
754 	return (fc_success_op(ap, rp, cp));
755 }
756 
757 /*
758  * Make sure any in-progress activity is completed,
759  * and for now, online the subtree.
760  * XXX: Presumably the configurator will online the subtree
761  * XXX: by doing an ndi_devi_online with NDI_CONFIG on the child
762  * XXX: if there is one.  For now, we're doing it here.
763  * XXX: For buses without a configurator (and thus no config child),
764  * XXX: we have to do it here.
765  *
766  */
767 static int
768 fco_validate(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
769 {
770 	rp->cdip_state = FC_CDIP_CONFIG;
771 
772 	cp->nresults = fc_int2cell(0);
773 	return (fc_success_op(ap, rp, cp));
774 }
775 
776 static void
777 remove_subtree(dev_info_t *root, struct fc_device_tree *subtree)
778 {
779 	dev_info_t *child;
780 
781 	/*
782 	 * Remove the subtree, depth first. Each iterative
783 	 * call gets another child at each level of the tree
784 	 * until there are no more children.
785 	 */
786 	while ((child = fc_child_node(root, subtree)) != NULL)
787 		remove_subtree(child, subtree);
788 
789 	/*
790 	 * Delete the subtree root and remove its record from our
791 	 * copy of the subtree.
792 	 */
793 	fc_remove_child(root, subtree);
794 	(void) ndi_devi_offline(root, NDI_UNCONFIG | NDI_DEVI_REMOVE);
795 }
796 
797 static int
798 fco_invalidate(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
799 {
800 	dev_info_t *root, *child;
801 	struct fc_device_tree *subtree = fc_handle_to_dtree(rp);
802 	int configured = (rp->cdip_state == FC_CDIP_CONFIG);
803 
804 	/*
805 	 * If we created any children, delete them. The root node is the
806 	 * config child, if one exists for this bus, otherwise it's the
807 	 * attachment point.
808 	 *
809 	 * Our copy of the subtree only contains records of nodes we created
810 	 * under the subtree root and contains the parent->child linkage
811 	 * that isn't yet established in the real device tree.
812 	 *
813 	 * XXX: What we don't do is restore the config child node to it's
814 	 * pre-interpretive state. (We may have added properties to
815 	 * that node. It's not clear if its necessary to clean them up.)
816 	 */
817 	root = rp->child ? rp->child : ap;
818 
819 	while ((child = fc_child_node(root, subtree)) != NULL) {
820 		FC_DEBUG2(1, CE_CONT, "fco_invalidate: remove subtree "
821 		    "<%s> dip %p\n", ddi_node_name(child), child);
822 		remove_subtree(child, subtree);
823 	}
824 
825 	if (configured)
826 		(void) ndi_devi_offline(root, NDI_UNCONFIG);
827 
828 	cp->nresults = fc_int2cell(0);
829 	return (fc_success_op(ap, rp, cp));
830 }
831 
832 static int
833 fco_exit(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
834 {
835 	FC_DEBUG0(1, CE_CONT, "exit op not implemented .. succeeding\n");
836 	cp->nresults = fc_int2cell(0);
837 	return (fc_success_op(ap, rp, cp));
838 }
839 
840 /*
841  * Needed to implement 'mac-address' Fcode, no obvious place to pick this
842  * info up from user-land.
843  */
844 static int
845 fco_local_ether_addr(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
846 {
847 	if (fc_cell2int(cp->nargs) != 0)
848 		return (fc_syntax_error(cp, "nargs must be 0"));
849 
850 	if (fc_cell2int(cp->nresults) != 2)
851 		return (fc_syntax_error(cp, "nresults must be 2"));
852 
853 	cp->nresults = fc_int2cell(2);
854 
855 	(void) localetheraddr(NULL, (struct ether_addr *)(&fc_result(cp, 0)));
856 
857 	return (fc_success_op(ap, rp, cp));
858 }
859 
860 #ifdef DEBUG
861 void
862 fc_debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3,
863 	uintptr_t a4, uintptr_t a5)
864 {
865 	cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5);
866 }
867 #endif
868