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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * Ported from 4.1.1_PSRA: "@(#)openprom.c 1.19 91/02/19 SMI";
28 *
29 * Porting notes:
30 *
31 * OPROMU2P unsupported after SunOS 4.x.
32 *
33 * Only one of these devices per system is allowed.
34 */
35
36 /*
37 * Openprom eeprom options/devinfo driver.
38 */
39
40 #include <sys/types.h>
41 #include <sys/errno.h>
42 #include <sys/file.h>
43 #include <sys/cmn_err.h>
44 #include <sys/kmem.h>
45 #include <sys/openpromio.h>
46 #include <sys/conf.h>
47 #include <sys/stat.h>
48 #include <sys/modctl.h>
49 #include <sys/debug.h>
50 #include <sys/autoconf.h>
51 #include <sys/ddi.h>
52 #include <sys/sunddi.h>
53 #include <sys/promif.h>
54 #include <sys/sysmacros.h> /* offsetof */
55 #include <sys/nvpair.h>
56 #include <sys/wanboot_impl.h>
57 #include <sys/zone.h>
58 #include <sys/consplat.h>
59 #include <sys/bootconf.h>
60 #include <sys/systm.h>
61 #include <sys/bootprops.h>
62
63 #define MAX_OPENS 32 /* Up to this many simultaneous opens */
64
65 #define IOC_IDLE 0 /* snapshot ioctl states */
66 #define IOC_SNAP 1 /* snapshot in progress */
67 #define IOC_DONE 2 /* snapshot done, but not copied out */
68 #define IOC_COPY 3 /* copyout in progress */
69
70 /*
71 * XXX Make this dynamic.. or (better still) make the interface stateless
72 */
73 static struct oprom_state {
74 pnode_t current_id; /* node we're fetching props from */
75 int16_t already_open; /* if true, this instance is 'active' */
76 int16_t ioc_state; /* snapshot ioctl state */
77 char *snapshot; /* snapshot of all prom nodes */
78 size_t size; /* size of snapshot */
79 prom_generation_cookie_t tree_gen;
80 } oprom_state[MAX_OPENS];
81
82 static kmutex_t oprom_lock; /* serialize instance assignment */
83
84 static int opromopen(dev_t *, int, int, cred_t *);
85 static int opromioctl(dev_t, int, intptr_t, int, cred_t *, int *);
86 static int opromclose(dev_t, int, int, cred_t *);
87
88 static int opinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
89 void **result);
90 static int opattach(dev_info_t *, ddi_attach_cmd_t cmd);
91 static int opdetach(dev_info_t *, ddi_detach_cmd_t cmd);
92
93 /* help functions */
94 static int oprom_checknodeid(pnode_t, pnode_t);
95 static int oprom_copyinstr(intptr_t, char *, size_t, size_t);
96 static int oprom_copynode(pnode_t, uint_t, char **, size_t *);
97 static int oprom_snapshot(struct oprom_state *, intptr_t);
98 static int oprom_copyout(struct oprom_state *, intptr_t);
99 static int oprom_setstate(struct oprom_state *, int16_t);
100
101 static struct cb_ops openeepr_cb_ops = {
102 opromopen, /* open */
103 opromclose, /* close */
104 nodev, /* strategy */
105 nodev, /* print */
106 nodev, /* dump */
107 nodev, /* read */
108 nodev, /* write */
109 opromioctl, /* ioctl */
110 nodev, /* devmap */
111 nodev, /* mmap */
112 nodev, /* segmap */
113 nochpoll, /* poll */
114 ddi_prop_op, /* prop_op */
115 NULL, /* streamtab */
116 D_NEW | D_MP /* Driver compatibility flag */
117 };
118
119 static struct dev_ops openeepr_ops = {
120 DEVO_REV, /* devo_rev, */
121 0, /* refcnt */
122 opinfo, /* info */
123 nulldev, /* identify */
124 nulldev, /* probe */
125 opattach, /* attach */
126 opdetach, /* detach */
127 nodev, /* reset */
128 &openeepr_cb_ops, /* driver operations */
129 NULL, /* bus operations */
130 NULL, /* power */
131 ddi_quiesce_not_needed, /* quiesce */
132 };
133
134 /*
135 * Module linkage information for the kernel.
136 */
137 static struct modldrv modldrv = {
138 &mod_driverops,
139 "OPENPROM/NVRAM Driver",
140 &openeepr_ops
141 };
142
143 static struct modlinkage modlinkage = {
144 MODREV_1,
145 { &modldrv, NULL }
146 };
147
148 int
149 _init(void)
150 {
151 int error;
152
153 mutex_init(&oprom_lock, NULL, MUTEX_DRIVER, NULL);
154
155 error = mod_install(&modlinkage);
156 if (error != 0) {
157 mutex_destroy(&oprom_lock);
158 return (error);
159 }
160
161 return (0);
162 }
163
164 int
165 _info(struct modinfo *modinfop)
166 {
167 return (mod_info(&modlinkage, modinfop));
168 }
169
170 int
171 _fini(void)
172 {
173 int error;
174
175 error = mod_remove(&modlinkage);
176 if (error != 0)
177 return (error);
178
179 mutex_destroy(&oprom_lock);
180 return (0);
181 }
182
183 static dev_info_t *opdip;
184 static pnode_t options_nodeid;
185
186 /*ARGSUSED*/
187 static int
188 opinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
189 {
190 int error = DDI_FAILURE;
191
192 switch (infocmd) {
193 case DDI_INFO_DEVT2DEVINFO:
194 *result = (void *)opdip;
195 error = DDI_SUCCESS;
196 break;
197 case DDI_INFO_DEVT2INSTANCE:
198 /* All dev_t's map to the same, single instance */
199 *result = (void *)0;
200 error = DDI_SUCCESS;
201 break;
202 default:
203 break;
204 }
205
206 return (error);
207 }
208
209 static int
210 opattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
211 {
212 switch (cmd) {
213
214 case DDI_ATTACH:
215 if (prom_is_openprom()) {
216 options_nodeid = prom_optionsnode();
217 } else {
218 options_nodeid = OBP_BADNODE;
219 }
220
221 opdip = dip;
222
223 if (ddi_create_minor_node(dip, "openprom", S_IFCHR,
224 0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
225 return (DDI_FAILURE);
226 }
227
228 return (DDI_SUCCESS);
229
230 default:
231 return (DDI_FAILURE);
232 }
233 }
234
235 static int
236 opdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
237 {
238 if (cmd != DDI_DETACH)
239 return (DDI_FAILURE);
240
241 ddi_remove_minor_node(dip, NULL);
242 opdip = NULL;
243
244 return (DDI_SUCCESS);
245 }
246
247 /*
248 * Allow multiple opens by tweaking the dev_t such that it looks like each
249 * open is getting a different minor device. Each minor gets a separate
250 * entry in the oprom_state[] table.
251 */
252 /*ARGSUSED*/
253 static int
254 opromopen(dev_t *devp, int flag, int otyp, cred_t *credp)
255 {
256 int m;
257 struct oprom_state *st = oprom_state;
258
259 if (getminor(*devp) != 0)
260 return (ENXIO);
261
262 mutex_enter(&oprom_lock);
263 for (m = 0; m < MAX_OPENS; m++)
264 if (st->already_open)
265 st++;
266 else {
267 st->already_open = 1;
268 /*
269 * It's ours.
270 */
271 st->current_id = (pnode_t)0;
272 ASSERT(st->snapshot == NULL && st->size == 0);
273 ASSERT(st->ioc_state == IOC_IDLE);
274 break;
275 }
276 mutex_exit(&oprom_lock);
277
278 if (m == MAX_OPENS) {
279 /*
280 * "Thank you for calling, but all our lines are
281 * busy at the moment.."
282 *
283 * We could get sophisticated here, and go into a
284 * sleep-retry loop .. but hey, I just can't see
285 * that many processes sitting in this driver.
286 *
287 * (And if it does become possible, then we should
288 * change the interface so that the 'state' is held
289 * external to the driver)
290 */
291 return (EAGAIN);
292 }
293
294 *devp = makedevice(getmajor(*devp), (minor_t)m);
295
296 return (0);
297 }
298
299 /*ARGSUSED*/
300 static int
301 opromclose(dev_t dev, int flag, int otype, cred_t *cred_p)
302 {
303 struct oprom_state *st;
304
305 st = &oprom_state[getminor(dev)];
306 ASSERT(getminor(dev) < MAX_OPENS && st->already_open != 0);
307 if (st->snapshot) {
308 kmem_free(st->snapshot, st->size);
309 st->snapshot = NULL;
310 st->size = 0;
311 st->ioc_state = IOC_IDLE;
312 }
313 mutex_enter(&oprom_lock);
314 st->already_open = 0;
315 mutex_exit(&oprom_lock);
316
317 return (0);
318 }
319
320 #ifdef __sparc
321 static int
322 get_bootpath_prop(char *bootpath)
323 {
324 if (root_is_ramdisk) {
325 if (BOP_GETPROP(bootops, "bootarchive", bootpath) == -1)
326 return (-1);
327 (void) strlcat(bootpath, ":a", BO_MAXOBJNAME);
328 } else {
329 if ((BOP_GETPROP(bootops, "bootpath", bootpath) == -1) ||
330 strlen(bootpath) == 0) {
331 if (BOP_GETPROP(bootops,
332 "boot-path", bootpath) == -1)
333 return (-1);
334 }
335 if (memcmp(bootpath, BP_ISCSI_DISK,
336 strlen(BP_ISCSI_DISK)) == 0) {
337 get_iscsi_bootpath_vhci(bootpath);
338 }
339 }
340 return (0);
341 }
342 #endif
343
344 struct opromioctl_args {
345 struct oprom_state *st;
346 int cmd;
347 intptr_t arg;
348 int mode;
349 };
350
351 /*ARGSUSED*/
352 static int
353 opromioctl_cb(void *avp, int has_changed)
354 {
355 struct opromioctl_args *argp = avp;
356 int cmd;
357 intptr_t arg;
358 int mode;
359 struct oprom_state *st;
360 struct openpromio *opp;
361 int valsize;
362 char *valbuf;
363 int error = 0;
364 uint_t userbufsize;
365 pnode_t node_id;
366 char propname[OBP_MAXPROPNAME];
367
368 st = argp->st;
369 cmd = argp->cmd;
370 arg = argp->arg;
371 mode = argp->mode;
372
373 if (has_changed) {
374 /*
375 * The prom tree has changed since we last used current_id,
376 * so we need to check it.
377 */
378 if ((st->current_id != OBP_NONODE) &&
379 (st->current_id != OBP_BADNODE)) {
380 if (oprom_checknodeid(st->current_id, OBP_NONODE) == 0)
381 st->current_id = OBP_BADNODE;
382 }
383 }
384
385 /*
386 * Check permissions
387 * and weed out unsupported commands on x86 platform
388 */
389 switch (cmd) {
390 #if !defined(__i386) && !defined(__amd64)
391 case OPROMLISTKEYSLEN:
392 valsize = prom_asr_list_keys_len();
393 opp = (struct openpromio *)kmem_zalloc(
394 sizeof (uint_t) + 1, KM_SLEEP);
395 opp->oprom_size = valsize;
396 if (copyout(opp, (void *)arg, (sizeof (uint_t))) != 0)
397 error = EFAULT;
398 kmem_free(opp, sizeof (uint_t) + 1);
399 break;
400 case OPROMLISTKEYS:
401 valsize = prom_asr_list_keys_len();
402 if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0)
403 return (EFAULT);
404 if (valsize > userbufsize)
405 return (EINVAL);
406 valbuf = (char *)kmem_zalloc(valsize + 1, KM_SLEEP);
407 if (prom_asr_list_keys((caddr_t)valbuf) == -1) {
408 kmem_free(valbuf, valsize + 1);
409 return (EFAULT);
410 }
411 opp = (struct openpromio *)kmem_zalloc(
412 valsize + sizeof (uint_t) + 1, KM_SLEEP);
413 opp->oprom_size = valsize;
414 bcopy(valbuf, opp->oprom_array, valsize);
415 if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0)
416 error = EFAULT;
417 kmem_free(valbuf, valsize + 1);
418 kmem_free(opp, valsize + sizeof (uint_t) + 1);
419 break;
420 case OPROMEXPORT:
421 valsize = prom_asr_export_len();
422 if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0)
423 return (EFAULT);
424 if (valsize > userbufsize)
425 return (EINVAL);
426 valbuf = (char *)kmem_zalloc(valsize + 1, KM_SLEEP);
427 if (prom_asr_export((caddr_t)valbuf) == -1) {
428 kmem_free(valbuf, valsize + 1);
429 return (EFAULT);
430 }
431 opp = (struct openpromio *)kmem_zalloc(
432 valsize + sizeof (uint_t) + 1, KM_SLEEP);
433 opp->oprom_size = valsize;
434 bcopy(valbuf, opp->oprom_array, valsize);
435 if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0)
436 error = EFAULT;
437 kmem_free(valbuf, valsize + 1);
438 kmem_free(opp, valsize + sizeof (uint_t) + 1);
439 break;
440 case OPROMEXPORTLEN:
441 valsize = prom_asr_export_len();
442 opp = (struct openpromio *)kmem_zalloc(
443 sizeof (uint_t) + 1, KM_SLEEP);
444 opp->oprom_size = valsize;
445 if (copyout(opp, (void *)arg, (sizeof (uint_t))) != 0)
446 error = EFAULT;
447 kmem_free(opp, sizeof (uint_t) + 1);
448 break;
449 #endif
450 case OPROMGETOPT:
451 case OPROMNXTOPT:
452 if ((mode & FREAD) == 0) {
453 return (EPERM);
454 }
455 node_id = options_nodeid;
456 break;
457
458 case OPROMSETOPT:
459 case OPROMSETOPT2:
460 #if !defined(__i386) && !defined(__amd64)
461 if (mode & FWRITE) {
462 node_id = options_nodeid;
463 break;
464 }
465 #endif /* !__i386 && !__amd64 */
466 return (EPERM);
467
468 case OPROMNEXT:
469 case OPROMCHILD:
470 case OPROMGETPROP:
471 case OPROMGETPROPLEN:
472 case OPROMNXTPROP:
473 case OPROMSETNODEID:
474 if ((mode & FREAD) == 0) {
475 return (EPERM);
476 }
477 node_id = st->current_id;
478 break;
479 case OPROMCOPYOUT:
480 if (st->snapshot == NULL)
481 return (EINVAL);
482 /*FALLTHROUGH*/
483 case OPROMSNAPSHOT:
484 case OPROMGETCONS:
485 case OPROMGETBOOTARGS:
486 case OPROMGETBOOTPATH:
487 case OPROMGETVERSION:
488 case OPROMPATH2DRV:
489 case OPROMPROM2DEVNAME:
490 #if !defined(__i386) && !defined(__amd64)
491 case OPROMGETFBNAME:
492 case OPROMDEV2PROMNAME:
493 case OPROMREADY64:
494 #endif /* !__i386 && !__amd64 */
495 if ((mode & FREAD) == 0) {
496 return (EPERM);
497 }
498 break;
499
500 #if !defined(__i386) && !defined(__amd64)
501 case WANBOOT_SETKEY:
502 if (!(mode & FWRITE))
503 return (EPERM);
504 break;
505 #endif /* !__i386 && !defined(__amd64) */
506
507 default:
508 return (EINVAL);
509 }
510
511 /*
512 * Deal with SNAPSHOT and COPYOUT ioctls first
513 */
514 switch (cmd) {
515 case OPROMCOPYOUT:
516 return (oprom_copyout(st, arg));
517
518 case OPROMSNAPSHOT:
519 return (oprom_snapshot(st, arg));
520 }
521
522 /*
523 * Copy in user argument length and allocation memory
524 *
525 * NB do not copyin the entire buffer we may not need
526 * to. userbufsize can be as big as 32 K.
527 */
528 if (copyin((void *)arg, &userbufsize, sizeof (uint_t)) != 0)
529 return (EFAULT);
530
531 if (userbufsize == 0 || userbufsize > OPROMMAXPARAM)
532 return (EINVAL);
533
534 opp = (struct openpromio *)kmem_zalloc(
535 userbufsize + sizeof (uint_t) + 1, KM_SLEEP);
536
537 /*
538 * Execute command
539 */
540 switch (cmd) {
541
542 case OPROMGETOPT:
543 case OPROMGETPROP:
544 case OPROMGETPROPLEN:
545
546 if ((prom_is_openprom() == 0) ||
547 (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) {
548 error = EINVAL;
549 break;
550 }
551
552 /*
553 * The argument, a NULL terminated string, is a prop name.
554 */
555 if ((error = oprom_copyinstr(arg, opp->oprom_array,
556 (size_t)userbufsize, OBP_MAXPROPNAME)) != 0) {
557 break;
558 }
559 (void) strcpy(propname, opp->oprom_array);
560 valsize = prom_getproplen(node_id, propname);
561
562 /*
563 * 4010173: 'name' is a property, but not an option.
564 */
565 if ((cmd == OPROMGETOPT) && (strcmp("name", propname) == 0))
566 valsize = -1;
567
568 if (cmd == OPROMGETPROPLEN) {
569 int proplen = valsize;
570
571 if (userbufsize < sizeof (int)) {
572 error = EINVAL;
573 break;
574 }
575 opp->oprom_size = valsize = sizeof (int);
576 bcopy(&proplen, opp->oprom_array, valsize);
577 } else if (valsize > 0 && valsize <= userbufsize) {
578 bzero(opp->oprom_array, valsize + 1);
579 (void) prom_getprop(node_id, propname,
580 opp->oprom_array);
581 opp->oprom_size = valsize;
582 if (valsize < userbufsize)
583 ++valsize; /* Forces NULL termination */
584 /* If space permits */
585 } else {
586 /*
587 * XXX: There is no error code if the buf is too small.
588 * which is consistent with the current behavior.
589 *
590 * NB: This clause also handles the non-error
591 * zero length (boolean) property value case.
592 */
593 opp->oprom_size = 0;
594 (void) strcpy(opp->oprom_array, "");
595 valsize = 1;
596 }
597 if (copyout(opp, (void *)arg, (valsize + sizeof (uint_t))) != 0)
598 error = EFAULT;
599 break;
600
601 case OPROMNXTOPT:
602 case OPROMNXTPROP:
603 if ((prom_is_openprom() == 0) ||
604 (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) {
605 error = EINVAL;
606 break;
607 }
608
609 /*
610 * The argument, a NULL terminated string, is a prop name.
611 */
612 if ((error = oprom_copyinstr(arg, opp->oprom_array,
613 (size_t)userbufsize, OBP_MAXPROPNAME)) != 0) {
614 break;
615 }
616 valbuf = (char *)prom_nextprop(node_id, opp->oprom_array,
617 propname);
618 valsize = strlen(valbuf);
619
620 /*
621 * 4010173: 'name' is a property, but it's not an option.
622 */
623 if ((cmd == OPROMNXTOPT) && valsize &&
624 (strcmp(valbuf, "name") == 0)) {
625 valbuf = (char *)prom_nextprop(node_id, "name",
626 propname);
627 valsize = strlen(valbuf);
628 }
629
630 if (valsize == 0) {
631 opp->oprom_size = 0;
632 } else if (++valsize <= userbufsize) {
633 opp->oprom_size = valsize;
634 bzero((caddr_t)opp->oprom_array, (size_t)valsize);
635 bcopy((caddr_t)valbuf, (caddr_t)opp->oprom_array,
636 (size_t)valsize);
637 }
638
639 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0)
640 error = EFAULT;
641 break;
642
643 case OPROMNEXT:
644 case OPROMCHILD:
645 case OPROMSETNODEID:
646
647 if (prom_is_openprom() == 0 ||
648 userbufsize < sizeof (pnode_t)) {
649 error = EINVAL;
650 break;
651 }
652
653 /*
654 * The argument is a phandle. (aka pnode_t)
655 */
656 if (copyin(((caddr_t)arg + sizeof (uint_t)),
657 opp->oprom_array, sizeof (pnode_t)) != 0) {
658 error = EFAULT;
659 break;
660 }
661
662 /*
663 * If pnode_t from userland is garbage, we
664 * could confuse the PROM.
665 */
666 node_id = *(pnode_t *)opp->oprom_array;
667 if (oprom_checknodeid(node_id, st->current_id) == 0) {
668 cmn_err(CE_NOTE, "!nodeid 0x%x not found",
669 (int)node_id);
670 error = EINVAL;
671 break;
672 }
673
674 if (cmd == OPROMNEXT)
675 st->current_id = prom_nextnode(node_id);
676 else if (cmd == OPROMCHILD)
677 st->current_id = prom_childnode(node_id);
678 else {
679 /* OPROMSETNODEID */
680 st->current_id = node_id;
681 break;
682 }
683
684 opp->oprom_size = sizeof (pnode_t);
685 *(pnode_t *)opp->oprom_array = st->current_id;
686
687 if (copyout(opp, (void *)arg,
688 sizeof (pnode_t) + sizeof (uint_t)) != 0)
689 error = EFAULT;
690 break;
691
692 case OPROMGETCONS:
693 /*
694 * Is openboot supported on this machine?
695 * This ioctl used to return the console device,
696 * information; this is now done via modctl()
697 * in libdevinfo.
698 */
699 opp->oprom_size = sizeof (char);
700
701 opp->oprom_array[0] |= prom_is_openprom() ?
702 OPROMCONS_OPENPROM : 0;
703
704 /*
705 * The rest of the info is needed by Install to
706 * decide if graphics should be started.
707 */
708 if ((getzoneid() == GLOBAL_ZONEID) &&
709 plat_stdin_is_keyboard()) {
710 opp->oprom_array[0] |= OPROMCONS_STDIN_IS_KBD;
711 }
712
713 if ((getzoneid() == GLOBAL_ZONEID) &&
714 plat_stdout_is_framebuffer()) {
715 opp->oprom_array[0] |= OPROMCONS_STDOUT_IS_FB;
716 }
717
718 if (copyout(opp, (void *)arg,
719 sizeof (char) + sizeof (uint_t)) != 0)
720 error = EFAULT;
721 break;
722
723 case OPROMGETBOOTARGS: {
724 extern char kern_bootargs[];
725
726 valsize = strlen(kern_bootargs) + 1;
727 if (valsize > userbufsize) {
728 error = EINVAL;
729 break;
730 }
731 (void) strcpy(opp->oprom_array, kern_bootargs);
732 opp->oprom_size = valsize - 1;
733
734 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0)
735 error = EFAULT;
736 break;
737 }
738
739 case OPROMGETBOOTPATH: {
740 #if defined(__sparc) && defined(_OBP)
741
742 char bpath[OBP_MAXPATHLEN];
743 if (get_bootpath_prop(bpath) != 0) {
744 error = EINVAL;
745 break;
746 }
747 valsize = strlen(bpath) + 1;
748 if (valsize > userbufsize) {
749 error = EINVAL;
750 break;
751 }
752 (void) strcpy(opp->oprom_array, bpath);
753
754 #elif defined(__i386) || defined(__amd64)
755
756 extern char saved_cmdline[];
757 valsize = strlen(saved_cmdline) + 1;
758 if (valsize > userbufsize) {
759 error = EINVAL;
760 break;
761 }
762 (void) strcpy(opp->oprom_array, saved_cmdline);
763 #endif
764 opp->oprom_size = valsize - 1;
765 if (copyout(opp, (void *)arg, valsize + sizeof (uint_t)) != 0)
766 error = EFAULT;
767 break;
768 }
769
770 /*
771 * convert a prom device path to an equivalent devfs path
772 */
773 case OPROMPROM2DEVNAME: {
774 char *dev_name;
775
776 /*
777 * The input argument, a pathname, is a NULL terminated string.
778 */
779 if ((error = oprom_copyinstr(arg, opp->oprom_array,
780 (size_t)userbufsize, MAXPATHLEN)) != 0) {
781 break;
782 }
783
784 dev_name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
785
786 error = i_promname_to_devname(opp->oprom_array, dev_name);
787 if (error != 0) {
788 kmem_free(dev_name, MAXPATHLEN);
789 break;
790 }
791 valsize = opp->oprom_size = strlen(dev_name);
792 if (++valsize > userbufsize) {
793 kmem_free(dev_name, MAXPATHLEN);
794 error = EINVAL;
795 break;
796 }
797 (void) strcpy(opp->oprom_array, dev_name);
798 if (copyout(opp, (void *)arg, sizeof (uint_t) + valsize) != 0)
799 error = EFAULT;
800
801 kmem_free(dev_name, MAXPATHLEN);
802 break;
803 }
804
805 /*
806 * Convert a prom device path name to a driver name
807 */
808 case OPROMPATH2DRV: {
809 char *drv_name;
810 major_t maj;
811
812 /*
813 * The input argument, a pathname, is a NULL terminated string.
814 */
815 if ((error = oprom_copyinstr(arg, opp->oprom_array,
816 (size_t)userbufsize, MAXPATHLEN)) != 0) {
817 break;
818 }
819
820 /*
821 * convert path to a driver binding name
822 */
823 maj = path_to_major((char *)opp->oprom_array);
824 if (maj == DDI_MAJOR_T_NONE) {
825 error = EINVAL;
826 break;
827 }
828
829 /*
830 * resolve any aliases
831 */
832 if ((drv_name = ddi_major_to_name(maj)) == NULL) {
833 error = EINVAL;
834 break;
835 }
836
837 (void) strcpy(opp->oprom_array, drv_name);
838 opp->oprom_size = strlen(drv_name);
839 if (copyout(opp, (void *)arg,
840 sizeof (uint_t) + opp->oprom_size + 1) != 0)
841 error = EFAULT;
842 break;
843 }
844
845 case OPROMGETVERSION:
846 /*
847 * Get a string representing the running version of the
848 * prom. How to create such a string is platform dependent,
849 * so we just defer to a promif function. If no such
850 * association exists, the promif implementation
851 * may copy the string "unknown" into the given buffer,
852 * and return its length (incl. NULL terminator).
853 *
854 * We expect prom_version_name to return the actual
855 * length of the string, but copy at most userbufsize
856 * bytes into the given buffer, including NULL termination.
857 */
858
859 valsize = prom_version_name(opp->oprom_array, userbufsize);
860 if (valsize < 0) {
861 error = EINVAL;
862 break;
863 }
864
865 /*
866 * copyout only the part of the user buffer we need to.
867 */
868 if (copyout(opp, (void *)arg,
869 (size_t)(min((uint_t)valsize, userbufsize) +
870 sizeof (uint_t))) != 0)
871 error = EFAULT;
872 break;
873
874 #if !defined(__i386) && !defined(__amd64)
875 case OPROMGETFBNAME:
876 /*
877 * Return stdoutpath, if it's a frame buffer.
878 * Yes, we are comparing a possibly longer string against
879 * the size we're really going to copy, but so what?
880 */
881 if ((getzoneid() == GLOBAL_ZONEID) &&
882 (prom_stdout_is_framebuffer() != 0) &&
883 (userbufsize > strlen(prom_stdoutpath()))) {
884 prom_strip_options(prom_stdoutpath(),
885 opp->oprom_array); /* strip options and copy */
886 valsize = opp->oprom_size = strlen(opp->oprom_array);
887 if (copyout(opp, (void *)arg,
888 valsize + 1 + sizeof (uint_t)) != 0)
889 error = EFAULT;
890 } else
891 error = EINVAL;
892 break;
893
894 /*
895 * Convert a logical or physical device path to prom device path
896 */
897 case OPROMDEV2PROMNAME: {
898 char *prom_name;
899
900 /*
901 * The input argument, a pathname, is a NULL terminated string.
902 */
903 if ((error = oprom_copyinstr(arg, opp->oprom_array,
904 (size_t)userbufsize, MAXPATHLEN)) != 0) {
905 break;
906 }
907
908 prom_name = kmem_alloc(userbufsize, KM_SLEEP);
909
910 /*
911 * convert the devfs path to an equivalent prom path
912 */
913 error = i_devname_to_promname(opp->oprom_array, prom_name,
914 userbufsize);
915
916 if (error != 0) {
917 kmem_free(prom_name, userbufsize);
918 break;
919 }
920
921 for (valsize = 0; valsize < userbufsize; valsize++) {
922 opp->oprom_array[valsize] = prom_name[valsize];
923
924 if ((valsize > 0) && (prom_name[valsize] == '\0') &&
925 (prom_name[valsize-1] == '\0')) {
926 break;
927 }
928 }
929 opp->oprom_size = valsize;
930
931 kmem_free(prom_name, userbufsize);
932 if (copyout(opp, (void *)arg, sizeof (uint_t) + valsize) != 0)
933 error = EFAULT;
934
935 break;
936 }
937
938 case OPROMSETOPT:
939 case OPROMSETOPT2: {
940 int namebuflen;
941 int valbuflen;
942
943 if ((prom_is_openprom() == 0) ||
944 (node_id == OBP_NONODE) || (node_id == OBP_BADNODE)) {
945 error = EINVAL;
946 break;
947 }
948
949 /*
950 * The arguments are a property name and a value.
951 * Copy in the entire user buffer.
952 */
953 if (copyin(((caddr_t)arg + sizeof (uint_t)),
954 opp->oprom_array, userbufsize) != 0) {
955 error = EFAULT;
956 break;
957 }
958
959 /*
960 * The property name is the first string, value second
961 */
962 namebuflen = strlen(opp->oprom_array);
963 valbuf = opp->oprom_array + namebuflen + 1;
964 valbuflen = strlen(valbuf);
965
966 if (cmd == OPROMSETOPT) {
967 valsize = valbuflen + 1; /* +1 for the '\0' */
968 } else {
969 if ((namebuflen + 1 + valbuflen + 1) > userbufsize) {
970 error = EINVAL;
971 break;
972 }
973 valsize = (opp->oprom_array + userbufsize) - valbuf;
974 }
975
976 /*
977 * 4010173: 'name' is not an option, but it is a property.
978 */
979 if (strcmp(opp->oprom_array, "name") == 0)
980 error = EINVAL;
981 else if (prom_setprop(node_id, opp->oprom_array,
982 valbuf, valsize) < 0)
983 error = EINVAL;
984
985 break;
986 }
987
988 case OPROMREADY64: {
989 struct openprom_opr64 *opr =
990 (struct openprom_opr64 *)opp->oprom_array;
991 int i;
992 pnode_t id;
993
994 if (userbufsize < sizeof (*opr)) {
995 error = EINVAL;
996 break;
997 }
998
999 valsize = userbufsize -
1000 offsetof(struct openprom_opr64, message);
1001
1002 i = prom_version_check(opr->message, valsize, &id);
1003 opr->return_code = i;
1004 opr->nodeid = (int)id;
1005
1006 valsize = offsetof(struct openprom_opr64, message);
1007 valsize += strlen(opr->message) + 1;
1008
1009 /*
1010 * copyout only the part of the user buffer we need to.
1011 */
1012 if (copyout(opp, (void *)arg,
1013 (size_t)(min((uint_t)valsize, userbufsize) +
1014 sizeof (uint_t))) != 0)
1015 error = EFAULT;
1016 break;
1017
1018 } /* case OPROMREADY64 */
1019
1020 case WANBOOT_SETKEY: {
1021 struct wankeyio *wp;
1022 int reslen;
1023 int status;
1024 int rv;
1025 int i;
1026
1027 /*
1028 * The argument is a struct wankeyio. Validate it as best
1029 * we can.
1030 */
1031 if (userbufsize != (sizeof (struct wankeyio))) {
1032 error = EINVAL;
1033 break;
1034 }
1035 if (copyin(((caddr_t)arg + sizeof (uint_t)),
1036 opp->oprom_array, sizeof (struct wankeyio)) != 0) {
1037 error = EFAULT;
1038 break;
1039 }
1040 wp = (struct wankeyio *)opp->oprom_array;
1041
1042 /* check for key name and key size overflow */
1043 for (i = 0; i < WANBOOT_MAXKEYNAMELEN; i++)
1044 if (wp->wk_keyname[i] == '\0')
1045 break;
1046 if ((i == WANBOOT_MAXKEYNAMELEN) ||
1047 (wp->wk_keysize > WANBOOT_MAXKEYLEN)) {
1048 error = EINVAL;
1049 break;
1050 }
1051
1052 rv = prom_set_security_key(wp->wk_keyname, wp->wk_u.key,
1053 wp->wk_keysize, &reslen, &status);
1054 if (rv)
1055 error = EIO;
1056 else
1057 switch (status) {
1058 case 0:
1059 error = 0;
1060 break;
1061
1062 case -2: /* out of key storage space */
1063 error = ENOSPC;
1064 break;
1065
1066 case -3: /* key name or value too long */
1067 error = EINVAL;
1068 break;
1069
1070 case -4: /* can't delete: no such key */
1071 error = ENOENT;
1072 break;
1073
1074 case -1: /* unspecified error */
1075 default: /* this should not happen */
1076 error = EIO;
1077 break;
1078 }
1079 break;
1080 } /* case WANBOOT_SETKEY */
1081 #endif /* !__i386 && !__amd64 */
1082 } /* switch (cmd) */
1083
1084 kmem_free(opp, userbufsize + sizeof (uint_t) + 1);
1085 return (error);
1086 }
1087
1088 /*ARGSUSED*/
1089 static int
1090 opromioctl(dev_t dev, int cmd, intptr_t arg, int mode,
1091 cred_t *credp, int *rvalp)
1092 {
1093 struct oprom_state *st;
1094 struct opromioctl_args arg_block;
1095
1096 if (getminor(dev) >= MAX_OPENS)
1097 return (ENXIO);
1098
1099 st = &oprom_state[getminor(dev)];
1100 ASSERT(st->already_open);
1101 arg_block.st = st;
1102 arg_block.cmd = cmd;
1103 arg_block.arg = arg;
1104 arg_block.mode = mode;
1105 return (prom_tree_access(opromioctl_cb, &arg_block, &st->tree_gen));
1106 }
1107
1108 /*
1109 * Copyin string and verify the actual string length is less than maxsize
1110 * specified by the caller.
1111 *
1112 * Currently, maxsize is either OBP_MAXPROPNAME for property names
1113 * or MAXPATHLEN for device path names. userbufsize is specified
1114 * by the userland caller.
1115 */
1116 static int
1117 oprom_copyinstr(intptr_t arg, char *buf, size_t bufsize, size_t maxsize)
1118 {
1119 int error;
1120 size_t actual_len;
1121
1122 if ((error = copyinstr(((caddr_t)arg + sizeof (uint_t)),
1123 buf, bufsize, &actual_len)) != 0) {
1124 return (error);
1125 }
1126 if ((actual_len == 0) || (actual_len > maxsize)) {
1127 return (EINVAL);
1128 }
1129
1130 return (0);
1131 }
1132
1133 /*
1134 * Check pnode_t passed in from userland
1135 */
1136 static int
1137 oprom_checknodeid(pnode_t node_id, pnode_t current_id)
1138 {
1139 int depth;
1140 pnode_t id[OBP_STACKDEPTH];
1141
1142 /*
1143 * optimized path
1144 */
1145 if (node_id == 0) {
1146 return (1);
1147 }
1148 if (node_id == OBP_BADNODE) {
1149 return (0);
1150 }
1151 if ((current_id != OBP_BADNODE) && ((node_id == current_id) ||
1152 (node_id == prom_nextnode(current_id)) ||
1153 (node_id == prom_childnode(current_id)))) {
1154 return (1);
1155 }
1156
1157 /*
1158 * long path: walk from root till we find node_id
1159 */
1160 depth = 1;
1161 id[0] = prom_nextnode((pnode_t)0);
1162
1163 while (depth) {
1164 if (id[depth - 1] == node_id)
1165 return (1); /* node_id found */
1166
1167 if (id[depth] = prom_childnode(id[depth - 1])) {
1168 depth++;
1169 continue;
1170 }
1171
1172 while (depth &&
1173 ((id[depth - 1] = prom_nextnode(id[depth - 1])) == 0))
1174 depth--;
1175 }
1176 return (0); /* node_id not found */
1177 }
1178
1179 static int
1180 oprom_copytree(struct oprom_state *st, uint_t flag)
1181 {
1182 ASSERT(st->snapshot == NULL && st->size == 0);
1183 return (oprom_copynode(
1184 prom_nextnode(0), flag, &st->snapshot, &st->size));
1185 }
1186
1187 static int
1188 oprom_snapshot(struct oprom_state *st, intptr_t arg)
1189 {
1190 uint_t flag;
1191
1192 if (oprom_setstate(st, IOC_SNAP) == -1)
1193 return (EBUSY);
1194
1195 /* copyin flag and create snapshot */
1196 if ((copyin((void *)arg, &flag, sizeof (uint_t)) != 0) ||
1197 (oprom_copytree(st, flag) != 0)) {
1198 (void) oprom_setstate(st, IOC_IDLE);
1199 return (EFAULT);
1200 }
1201
1202
1203 /* copyout the size of the snapshot */
1204 flag = (uint_t)st->size;
1205 if (copyout(&flag, (void *)arg, sizeof (uint_t)) != 0) {
1206 kmem_free(st->snapshot, st->size);
1207 st->snapshot = NULL;
1208 st->size = 0;
1209 (void) oprom_setstate(st, IOC_IDLE);
1210 return (EFAULT);
1211 }
1212
1213 (void) oprom_setstate(st, IOC_DONE);
1214 return (0);
1215 }
1216
1217 static int
1218 oprom_copyout(struct oprom_state *st, intptr_t arg)
1219 {
1220 int error = 0;
1221 uint_t size;
1222
1223 if (oprom_setstate(st, IOC_COPY) == -1)
1224 return (EBUSY);
1225
1226 /* copyin size and copyout snapshot */
1227 if (copyin((void *)arg, &size, sizeof (uint_t)) != 0)
1228 error = EFAULT;
1229 else if (size < st->size)
1230 error = EINVAL;
1231 else if (copyout(st->snapshot, (void *)arg, st->size) != 0)
1232 error = EFAULT;
1233
1234 if (error) {
1235 /*
1236 * on error keep the snapshot until a successful
1237 * copyout or when the driver is closed.
1238 */
1239 (void) oprom_setstate(st, IOC_DONE);
1240 return (error);
1241 }
1242
1243 kmem_free(st->snapshot, st->size);
1244 st->snapshot = NULL;
1245 st->size = 0;
1246 (void) oprom_setstate(st, IOC_IDLE);
1247 return (0);
1248 }
1249
1250 /*
1251 * Copy all properties of nodeid into a single packed nvlist
1252 */
1253 static int
1254 oprom_copyprop(pnode_t nodeid, uint_t flag, nvlist_t *nvl)
1255 {
1256 int proplen;
1257 char *propname, *propval, *buf1, *buf2;
1258
1259 ASSERT(nvl != NULL);
1260
1261 /*
1262 * non verbose mode, get the "name" property only
1263 */
1264 if (flag == 0) {
1265 proplen = prom_getproplen(nodeid, "name");
1266 if (proplen <= 0) {
1267 cmn_err(CE_WARN,
1268 "failed to get the name of openprom node 0x%x",
1269 nodeid);
1270 (void) nvlist_add_string(nvl, "name", "");
1271 return (0);
1272 }
1273 propval = kmem_zalloc(proplen + 1, KM_SLEEP);
1274 (void) prom_getprop(nodeid, "name", propval);
1275 (void) nvlist_add_string(nvl, "name", propval);
1276 kmem_free(propval, proplen + 1);
1277 return (0);
1278 }
1279
1280 /*
1281 * Ask for first property by passing a NULL string
1282 */
1283 buf1 = kmem_alloc(OBP_MAXPROPNAME, KM_SLEEP);
1284 buf2 = kmem_zalloc(OBP_MAXPROPNAME, KM_SLEEP);
1285 buf1[0] = '\0';
1286 while (propname = (char *)prom_nextprop(nodeid, buf1, buf2)) {
1287 if (strlen(propname) == 0)
1288 break; /* end of prop list */
1289 (void) strcpy(buf1, propname);
1290
1291 proplen = prom_getproplen(nodeid, propname);
1292 if (proplen == 0) {
1293 /* boolean property */
1294 (void) nvlist_add_boolean(nvl, propname);
1295 continue;
1296 }
1297 /* add 1 for null termination in case of a string */
1298 propval = kmem_zalloc(proplen + 1, KM_SLEEP);
1299 (void) prom_getprop(nodeid, propname, propval);
1300 (void) nvlist_add_byte_array(nvl, propname,
1301 (uchar_t *)propval, proplen + 1);
1302 kmem_free(propval, proplen + 1);
1303 bzero(buf2, OBP_MAXPROPNAME);
1304 }
1305
1306 kmem_free(buf1, OBP_MAXPROPNAME);
1307 kmem_free(buf2, OBP_MAXPROPNAME);
1308
1309 return (0);
1310 }
1311
1312 /*
1313 * Copy all children and descendents into a a packed nvlist
1314 */
1315 static int
1316 oprom_copychild(pnode_t nodeid, uint_t flag, char **buf, size_t *size)
1317 {
1318 nvlist_t *nvl;
1319 pnode_t child = prom_childnode(nodeid);
1320
1321 if (child == 0)
1322 return (0);
1323
1324 (void) nvlist_alloc(&nvl, 0, KM_SLEEP);
1325 while (child != 0) {
1326 char *nodebuf = NULL;
1327 size_t nodesize = 0;
1328 if (oprom_copynode(child, flag, &nodebuf, &nodesize)) {
1329 nvlist_free(nvl);
1330 cmn_err(CE_WARN, "failed to copy nodeid 0x%x", child);
1331 return (-1);
1332 }
1333 (void) nvlist_add_byte_array(nvl, "node",
1334 (uchar_t *)nodebuf, nodesize);
1335 kmem_free(nodebuf, nodesize);
1336 child = prom_nextnode(child);
1337 }
1338
1339 (void) nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP);
1340 nvlist_free(nvl);
1341 return (0);
1342 }
1343
1344 /*
1345 * Copy a node into a packed nvlist
1346 */
1347 static int
1348 oprom_copynode(pnode_t nodeid, uint_t flag, char **buf, size_t *size)
1349 {
1350 int error = 0;
1351 nvlist_t *nvl;
1352 char *childlist = NULL;
1353 size_t childsize = 0;
1354
1355 (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP);
1356 ASSERT(nvl != NULL);
1357
1358 /* @nodeid -- @ is not a legal char in a 1275 property name */
1359 (void) nvlist_add_int32(nvl, "@nodeid", (int32_t)nodeid);
1360
1361 /* properties */
1362 if (error = oprom_copyprop(nodeid, flag, nvl))
1363 goto fail;
1364
1365 /* children */
1366 error = oprom_copychild(nodeid, flag, &childlist, &childsize);
1367 if (error != 0)
1368 goto fail;
1369 if (childlist != NULL) {
1370 (void) nvlist_add_byte_array(nvl, "@child",
1371 (uchar_t *)childlist, (uint_t)childsize);
1372 kmem_free(childlist, childsize);
1373 }
1374
1375 /* pack into contiguous buffer */
1376 error = nvlist_pack(nvl, buf, size, NV_ENCODE_NATIVE, KM_SLEEP);
1377
1378 fail:
1379 nvlist_free(nvl);
1380 return (error);
1381 }
1382
1383 /*
1384 * The driver is stateful across OPROMSNAPSHOT and OPROMCOPYOUT.
1385 * This function encapsulates the state machine:
1386 *
1387 * -> IOC_IDLE -> IOC_SNAP -> IOC_DONE -> IOC_COPY ->
1388 * | SNAPSHOT COPYOUT |
1389 * --------------------------------------------------
1390 *
1391 * Returns 0 on success and -1 on failure
1392 */
1393 static int
1394 oprom_setstate(struct oprom_state *st, int16_t new_state)
1395 {
1396 int ret = 0;
1397
1398 mutex_enter(&oprom_lock);
1399 switch (new_state) {
1400 case IOC_IDLE:
1401 case IOC_DONE:
1402 break;
1403 case IOC_SNAP:
1404 if (st->ioc_state != IOC_IDLE)
1405 ret = -1;
1406 break;
1407 case IOC_COPY:
1408 if (st->ioc_state != IOC_DONE)
1409 ret = -1;
1410 break;
1411 default:
1412 ret = -1;
1413 }
1414
1415 if (ret == 0)
1416 st->ioc_state = new_state;
1417 else
1418 cmn_err(CE_NOTE, "incorrect state transition from %d to %d",
1419 st->ioc_state, new_state);
1420 mutex_exit(&oprom_lock);
1421 return (ret);
1422 }