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