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
fc_ops_alloc_handle(dev_info_t * ap,dev_info_t * child,void * fcode,size_t fcode_size,char * unit_address,void * bus_args)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
fc_ops_free_handle(fco_handle_t rp)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
fc_ops(dev_info_t * ap,fco_handle_t handle,fc_ci_t * cp)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
fco_getproplen(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)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
fco_getprop(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)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
fco_ap_phandle(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)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
fco_child(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)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
fco_peer(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)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
fco_parent(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)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
fco_alloc_phandle(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)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 *
find_nodeid_resource(fco_handle_t rp,int n)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
fco_new_device(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)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
fco_finish_device(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)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
fco_create_property(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)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
fco_validate(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)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
remove_subtree(dev_info_t * root,struct fc_device_tree * subtree)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
fco_invalidate(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)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
fco_exit(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)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
fco_local_ether_addr(dev_info_t * ap,fco_handle_t rp,fc_ci_t * cp)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
fc_debug(char * fmt,uintptr_t a1,uintptr_t a2,uintptr_t a3,uintptr_t a4,uintptr_t a5)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