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