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
44static int fco_new_device(dev_info_t *, fco_handle_t, fc_ci_t *);
45static int fco_finish_device(dev_info_t *, fco_handle_t, fc_ci_t *);
46static int fco_create_property(dev_info_t *, fco_handle_t, fc_ci_t *);
47
48static int fco_validate(dev_info_t *, fco_handle_t, fc_ci_t *);
49static int fco_invalidate(dev_info_t *, fco_handle_t, fc_ci_t *);
50static int fco_exit(dev_info_t *, fco_handle_t, fc_ci_t *);
51
52static int fco_getproplen(dev_info_t *, fco_handle_t, fc_ci_t *);
53static int fco_getprop(dev_info_t *, fco_handle_t, fc_ci_t *);
54
55static int fco_ap_phandle(dev_info_t *, fco_handle_t, fc_ci_t *);
56static int fco_child(dev_info_t *, fco_handle_t, fc_ci_t *);
57static int fco_peer(dev_info_t *, fco_handle_t, fc_ci_t *);
58static int fco_parent(dev_info_t *, fco_handle_t, fc_ci_t *);
59static int fco_alloc_phandle(dev_info_t *, fco_handle_t, fc_ci_t *);
60
61static int fco_local_ether_addr(dev_info_t *, fco_handle_t, fc_ci_t *);
62
63struct fc_ops_v {
64	char *svc_name;
65	fc_ops_t *f;
66};
67
68static 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*/
100fco_handle_t
101fc_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 */
133void
134fc_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
163int
164fc_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*/
199static int
200fco_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*/
246static int
247fco_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
299static int
300fco_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
318static int
319fco_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
350static int
351fco_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
382static int
383fco_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 */
417static int
418fco_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
448static struct fc_resource *
449find_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 */
468static int
469fco_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 */
642static int
643fco_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 */
686static int
687fco_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 */
767static int
768fco_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
776static void
777remove_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
797static int
798fco_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
832static int
833fco_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 */
844static int
845fco_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
861void
862fc_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