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
45static int fco_new_device(dev_info_t *, fco_handle_t, fc_ci_t *);
46static int fco_finish_device(dev_info_t *, fco_handle_t, fc_ci_t *);
47static int fco_create_property(dev_info_t *, fco_handle_t, fc_ci_t *);
48
49static int fco_validate(dev_info_t *, fco_handle_t, fc_ci_t *);
50static int fco_invalidate(dev_info_t *, fco_handle_t, fc_ci_t *);
51static int fco_exit(dev_info_t *, fco_handle_t, fc_ci_t *);
52
53static int fco_getproplen(dev_info_t *, fco_handle_t, fc_ci_t *);
54static int fco_getprop(dev_info_t *, fco_handle_t, fc_ci_t *);
55
56static int fco_ap_phandle(dev_info_t *, fco_handle_t, fc_ci_t *);
57static int fco_child(dev_info_t *, fco_handle_t, fc_ci_t *);
58static int fco_peer(dev_info_t *, fco_handle_t, fc_ci_t *);
59static int fco_parent(dev_info_t *, fco_handle_t, fc_ci_t *);
60static int fco_alloc_phandle(dev_info_t *, fco_handle_t, fc_ci_t *);
61
62static int fco_local_ether_addr(dev_info_t *, fco_handle_t, fc_ci_t *);
63
64struct fc_ops_v {
65	char *svc_name;
66	fc_ops_t *f;
67};
68
69static 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*/
101fco_handle_t
102fc_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 */
134void
135fc_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
164int
165fc_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*/
200static int
201fco_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*/
247static int
248fco_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
300static int
301fco_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
319static int
320fco_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
351static int
352fco_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
383static int
384fco_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 */
418static int
419fco_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
449static struct fc_resource *
450find_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 */
469static int
470fco_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 */
643static int
644fco_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 */
687static int
688fco_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 */
768static int
769fco_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
777static void
778remove_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
798static int
799fco_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
833static int
834fco_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 */
845static int
846fco_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
862void
863fc_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