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