1 /* $NetBSD: promlib.c,v 1.45 2016/04/01 20:21:45 palle Exp $ */
2
3 /*-
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Paul Kranenburg.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * OPENPROM functions. These are here mainly to hide the OPENPROM interface
34 * from the rest of the kernel.
35 */
36
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: promlib.c,v 1.45 2016/04/01 20:21:45 palle Exp $");
39
40 #if defined(_KERNEL_OPT)
41 #include "opt_sparc_arch.h"
42 #endif
43
44 #include <sys/param.h>
45 #include <sys/kernel.h>
46
47 #ifdef _STANDALONE
48 #include <lib/libsa/stand.h>
49 #define malloc(s,t,f) alloc(s)
50 #else
51 #include <sys/systm.h>
52 #include <sys/malloc.h>
53 #endif /* _STANDALONE */
54
55 #include <machine/oldmon.h>
56 #include <machine/promlib.h>
57 #include <machine/ctlreg.h>
58 #include <sparc/sparc/asm.h>
59
60 #include <lib/libkern/libkern.h>
61
62 #define obpvec ((struct promvec *)romp)
63
64 static void notimplemented(void);
65 static void obp_v0_fortheval(const char *);
66 static void obp_set_callback(void (*)(void));
67 static int obp_v0_read(int, void *, int);
68 static int obp_v0_write(int, const void *, int);
69 static int obp_v2_getchar(void);
70 static int obp_v2_peekchar(void);
71 static void obp_v2_putchar(int);
72 static void obp_v2_putstr(const char *, int);
73 static int obp_v2_seek(int, u_quad_t);
74 static char *parse_bootfile(char *);
75 static char *parse_bootargs(char *);
76 static const char *obp_v0_getbootpath(void);
77 static const char *obp_v0_getbootfile(void);
78 static const char *obp_v0_getbootargs(void);
79 static const char *obp_v2_getbootpath(void);
80 static const char *obp_v2_getbootfile(void);
81 static const char *obp_v2_getbootargs(void);
82 static int obp_v2_finddevice(const char *);
83 static int obp_ticks(void);
84
85 static int findchosen(void);
86 static const char *opf_getbootpath(void);
87 static const char *opf_getbootfile(void);
88 static const char *opf_getbootargs(void);
89 static int opf_finddevice(const char *);
90 static int opf_instance_to_package(int);
91 static char *opf_nextprop(int, const char *);
92 static void opf_interpret_simple(const char *);
93
94
95 /*
96 * PROM entry points.
97 * Note: only PROM functions we use ar represented here; add as required.
98 */
99 struct promops promops = {
100 -1, /* version */
101 -1, /* revision */
102 -1, /* stdin handle */
103 -1, /* stdout handle */
104 NULL, /* bootargs */
105
106 (void *)notimplemented, /* bootpath */
107 (void *)notimplemented, /* bootargs */
108 (void *)notimplemented, /* bootfile */
109
110 (void *)notimplemented, /* getchar */
111 (void *)notimplemented, /* peekchar */
112 (void *)notimplemented, /* putchar */
113 (void *)notimplemented, /* putstr */
114 (void *)notimplemented, /* open */
115 (void *)notimplemented, /* close */
116 (void *)notimplemented, /* read */
117 (void *)notimplemented, /* write */
118 (void *)notimplemented, /* seek */
119
120 (void *)notimplemented, /* instance_to_package */
121
122 (void *)notimplemented, /* halt */
123 (void *)notimplemented, /* boot */
124 (void *)notimplemented, /* call */
125 (void *)notimplemented, /* interpret */
126 (void *)notimplemented, /* callback */
127 (void *)notimplemented, /* ticks */
128 NULL, /* ticker data */
129
130 (void *)notimplemented, /* setcontext */
131 (void *)notimplemented, /* cpustart */
132 (void *)notimplemented, /* cpustop */
133 (void *)notimplemented, /* cpuidle */
134 (void *)notimplemented, /* cpuresume */
135
136 (void *)notimplemented, /* firstchild */
137 (void *)notimplemented, /* nextsibling */
138
139 (void *)notimplemented, /* getproplen */
140 (void *)notimplemented, /* getprop */
141 (void *)notimplemented, /* setprop */
142 (void *)notimplemented, /* nextprop */
143 (void *)notimplemented /* finddevice */
144 };
145
146 static void
notimplemented(void)147 notimplemented(void)
148 {
149 char str[64];
150 int n;
151
152 n = snprintf(str, sizeof(str),
153 "Operation not implemented on ROM version %d\r\n",
154 promops.po_version);
155
156 /*
157 * Use PROM vector directly, in case we're called before prom_init().
158 */
159 #if defined(SUN4)
160 if (CPU_ISSUN4) {
161 struct om_vector *sun4pvec = (struct om_vector *)PROM_BASE;
162 (*sun4pvec->fbWriteStr)(str, n);
163 } else
164 #endif
165 if (obpvec->pv_magic == OBP_MAGIC) {
166 if (obpvec->pv_romvec_vers < 2) {
167 (*obpvec->pv_putstr)(str, n);
168 } else {
169 int fd = *obpvec->pv_v2bootargs.v2_fd1;
170 (*obpvec->pv_v2devops.v2_write)(fd, str, n);
171 }
172 } else { /* assume OFW */
173 static int stdout_node;
174 if (stdout_node == 0) {
175 int chosen = findchosen();
176 OF_getprop(chosen, "stdout", &stdout_node, sizeof(int));
177 }
178 OF_write(stdout_node, str, n);
179 }
180 }
181
182
183 /*
184 * prom_getprop() reads the named property data from a given node.
185 * A buffer for the data may be passed in `*bufp'; if NULL, a
186 * buffer is allocated. The argument `size' specifies the data
187 * element size of the property data. This function checks that
188 * the actual property data length is an integral multiple of
189 * the element size. The number of data elements read into the
190 * buffer is returned into the integer pointed at by `nitem'.
191 */
192
193 int
prom_getprop(int node,const char * name,size_t size,int * nitem,void * bufp)194 prom_getprop(int node, const char *name, size_t size, int *nitem, void *bufp)
195 {
196 void *buf;
197 int len;
198
199 len = prom_getproplen(node, name);
200 if (len <= 0)
201 return (ENOENT);
202
203 if ((len % size) != 0)
204 return (EINVAL);
205
206 buf = *(void **)bufp;
207 if (buf == NULL) {
208 /* No storage provided, so we allocate some */
209 buf = malloc(len, M_DEVBUF, M_NOWAIT);
210 if (buf == NULL)
211 return (ENOMEM);
212 } else {
213 if (size * (*nitem) < len)
214 return (ENOMEM);
215 }
216
217 _prom_getprop(node, name, buf, len);
218 *(void **)bufp = buf;
219 *nitem = len / size;
220 return (0);
221 }
222
223 /*
224 * Return a string property. There is a (small) limit on the length;
225 * the string is fetched into a static buffer which is overwritten on
226 * subsequent calls.
227 */
228 char *
prom_getpropstring(int node,const char * name)229 prom_getpropstring(int node, const char *name)
230 {
231 static char stringbuf[32];
232
233 return (prom_getpropstringA(node, name, stringbuf, sizeof stringbuf));
234 }
235
236 /*
237 * Alternative prom_getpropstring(), where caller provides the buffer
238 */
239 char *
prom_getpropstringA(int node,const char * name,char * buf,size_t bufsize)240 prom_getpropstringA(int node, const char *name, char *buf, size_t bufsize)
241 {
242 int len = bufsize - 1;
243
244 if (prom_getprop(node, name, 1, &len, &buf) != 0)
245 len = 0;
246
247 buf[len] = '\0'; /* usually unnecessary */
248 return (buf);
249 }
250
251 /*
252 * Fetch an integer (or pointer) property.
253 * The return value is the property, or the default if there was none.
254 */
255 int
prom_getpropint(int node,const char * name,int deflt)256 prom_getpropint(int node, const char *name, int deflt)
257 {
258 int intbuf, *ip = &intbuf;
259 int len = 1;
260
261 if (prom_getprop(node, name, sizeof(int), &len, &ip) != 0)
262 return (deflt);
263
264 return (*ip);
265 }
266
267 /*
268 * Node Name Matching per IEEE 1275, section 4.3.6.
269 */
270 static int
prom_matchname(int node,const char * name)271 prom_matchname(int node, const char *name)
272 {
273 char buf[32], *cp;
274
275 prom_getpropstringA(node, "name", buf, sizeof buf);
276 if (strcmp(buf, name) == 0)
277 /* Exact match */
278 return (1);
279
280 /* If name has a comma, an exact match is required */
281 if (strchr(name, ','))
282 return (0);
283
284 /*
285 * Otherwise, if the node's name contains a comma, we can match
286 * against the trailing string defined by the first comma.
287 */
288 if ((cp = strchr(buf, ',')) != NULL) {
289 if (strcmp(cp + 1, name) == 0)
290 return (1);
291 }
292
293 return (0);
294 }
295
296 /*
297 * Translate device path to node
298 */
299 int
prom_opennode(const char * path)300 prom_opennode(const char *path)
301 {
302 int fd;
303
304 if (prom_version() < 2) {
305 printf("WARNING: opennode not valid on PROM version %d\n",
306 promops.po_version);
307 return (0);
308 }
309 fd = prom_open(path);
310 if (fd == 0)
311 return (0);
312
313 return (prom_instance_to_package(fd));
314 }
315
316 int
prom_findroot(void)317 prom_findroot(void)
318 {
319 static int rootnode;
320 int node;
321
322 if ((node = rootnode) == 0 && (node = prom_nextsibling(0)) == 0)
323 panic("no PROM root device");
324 rootnode = node;
325 return (node);
326 }
327
328 /*
329 * Given a `first child' node number, locate the node with the given name.
330 * Return the node number, or 0 if not found.
331 */
332 int
prom_findnode(int first,const char * name)333 prom_findnode(int first, const char *name)
334 {
335 int node;
336
337 for (node = first; node != 0; node = prom_nextsibling(node)) {
338 if (prom_matchname(node, name))
339 return (node);
340 }
341 return (0);
342 }
343
344 /*
345 * Determine whether a node has the given property.
346 */
347 int
prom_node_has_property(int node,const char * prop)348 prom_node_has_property(int node, const char *prop)
349 {
350
351 return (prom_getproplen(node, prop) != -1);
352 }
353
354 /*
355 * prom_search() recursively searches a PROM subtree for a given node name
356 * See IEEE 1275 `Search for matching child node', section 4.3.3.
357 */
358 int
prom_search(int node,const char * name)359 prom_search(int node, const char *name)
360 {
361
362 if (node == 0)
363 node = prom_findroot();
364
365 if (prom_matchname(node, name))
366 return (node);
367
368 for (node = prom_firstchild(node); node != 0;
369 node = prom_nextsibling(node)) {
370 int cnode;
371 if ((cnode = prom_search(node, name)) != 0)
372 return (cnode);
373 }
374
375 return (0);
376 }
377
378 /*
379 * Find the named device in the PROM device tree.
380 * XXX - currently we discard any qualifiers attached to device component names
381 */
382 int
obp_v2_finddevice(const char * path)383 obp_v2_finddevice(const char *path)
384 {
385 int node;
386 char component[64];
387 char c, *cp;
388 const char *startp, *endp;
389 #define IS_SEP(c) ((c) == '/' || (c) == '@' || (c) == ':')
390
391 if (path == NULL)
392 return (-1);
393
394 node = prom_findroot();
395
396 for (startp = path; *startp != '\0'; ) {
397 /*
398 * Identify next component in path
399 */
400 while (*startp == '/')
401 startp++;
402
403 endp = startp;
404 while ((c = *endp) != '\0' && !IS_SEP(c))
405 endp++;
406
407 /* Copy component */
408 for (cp = component; startp != endp;) {
409 /* Check component bounds */
410 if (cp > component + sizeof component - 1)
411 return (-1);
412 *cp++ = *startp++;
413 }
414
415 /* Zero terminate this component */
416 *cp = '\0';
417
418 /* Advance `startp' over any non-slash separators */
419 while ((c = *startp) != '\0' && c != '/')
420 startp++;
421
422 node = prom_findnode(prom_firstchild(node), component);
423 if (node == 0)
424 return (-1);
425 }
426
427 return (node);
428 }
429
430
431 /*
432 * Get the global "options" node Id.
433 */
prom_getoptionsnode(void)434 int prom_getoptionsnode(void)
435 {
436 static int optionsnode;
437
438 if (optionsnode == 0) {
439 optionsnode = prom_findnode(prom_firstchild(prom_findroot()),
440 "options");
441 }
442 return optionsnode;
443 }
444
445 /*
446 * Return a property string value from the global "options" node.
447 */
prom_getoption(const char * name,char * buf,int buflen)448 int prom_getoption(const char *name, char *buf, int buflen)
449 {
450 int node = prom_getoptionsnode();
451 int error, len;
452
453 if (buflen == 0)
454 return (EINVAL);
455
456 if (node == 0)
457 return (ENOENT);
458
459 len = buflen - 1;
460 if ((error = prom_getprop(node, name, 1, &len, &buf)) != 0)
461 return error;
462
463 buf[len] = '\0';
464 return (0);
465 }
466
467 void
prom_halt(void)468 prom_halt(void)
469 {
470
471 prom_setcallback(NULL);
472 _prom_halt();
473 panic("PROM exit failed");
474 }
475
476 void
prom_boot(char * str)477 prom_boot(char *str)
478 {
479
480 prom_setcallback(NULL);
481 _prom_boot(str);
482 panic("PROM boot failed");
483 }
484
485
486 /*
487 * print debug info to prom.
488 * This is not safe, but then what do you expect?
489 */
490 void
prom_printf(const char * fmt,...)491 prom_printf(const char *fmt, ...)
492 {
493 static char buf[256];
494 int i, len;
495 va_list ap;
496
497 va_start(ap, fmt);
498 len = vsnprintf(buf, sizeof(buf), fmt, ap);
499 va_end(ap);
500
501 #if _obp_not_cooked_
502 (*promops.po_write)(promops.po_stdout, buf, len);
503 #endif
504
505 for (i = 0; i < len; i++) {
506 int c = buf[i];
507 if (c == '\n')
508 (*promops.po_putchar)('\r');
509 (*promops.po_putchar)(c);
510 }
511 }
512
513
514 /*
515 * Pass a string to the FORTH PROM to be interpreted.
516 * (Note: may fail silently)
517 */
518 static void
obp_v0_fortheval(const char * s)519 obp_v0_fortheval(const char *s)
520 {
521
522 obpvec->pv_fortheval.v0_eval(strlen(s), s);
523 }
524
525 int
obp_v0_read(int fd,void * buf,int len)526 obp_v0_read(int fd, void *buf, int len)
527 {
528 if (fd != prom_stdin())
529 prom_printf("obp_v0_read: unimplemented read from %d\n", fd);
530 return (-1);
531 }
532
533 int
obp_v0_write(int fd,const void * buf,int len)534 obp_v0_write(int fd, const void *buf, int len)
535 {
536 if (fd != prom_stdout())
537 prom_printf("obp_v0_write: unimplemented write on %d\n", fd);
538 (*obpvec->pv_putstr)(buf, len);
539 return (-1);
540 }
541
542 inline void
obp_v2_putchar(int c)543 obp_v2_putchar(int c)
544 {
545 char c0;
546
547 c0 = (c & 0x7f);
548 (*promops.po_write)(promops.po_stdout, &c0, 1);
549 }
550
551 #if 0
552 void
553 obp_v2_putchar_cooked(int c)
554 {
555
556 if (c == '\n')
557 obp_v2_putchar('\r');
558 obp_v2_putchar(c);
559 }
560 #endif
561
562 int
obp_v2_getchar(void)563 obp_v2_getchar(void)
564 {
565 char c;
566 int n;
567
568 while ((n = (*promops.po_read)(promops.po_stdin, &c, 1)) != 1)
569 /*void*/;
570 if (c == '\r')
571 c = '\n';
572 return (c);
573 }
574
575 int
obp_v2_peekchar(void)576 obp_v2_peekchar(void)
577 {
578 char c;
579 int n;
580
581 n = (*promops.po_read)(promops.po_stdin, &c, 1);
582 if (n < 0)
583 return (-1);
584
585 if (c == '\r')
586 c = '\n';
587 return (c);
588 }
589
590 int
obp_v2_seek(int handle,u_quad_t offset)591 obp_v2_seek(int handle, u_quad_t offset)
592 {
593 uint32_t hi, lo;
594
595 lo = offset & ((uint32_t)-1);
596 hi = (offset >> 32) & ((uint32_t)-1);
597 (*obpvec->pv_v2devops.v2_seek)(handle, hi, lo);
598 return (0);
599 }
600
601 /*
602 * On SS1s (and also IPCs, SLCs), `promvec->pv_v0bootargs->ba_argv[1]'
603 * contains the flags that were given after the boot command. On SS2s
604 * (and ELCs, IPXs, etc. and any sun4m class machine), `pv_v0bootargs'
605 * is NULL but `*promvec->pv_v2bootargs.v2_bootargs' points to
606 * "netbsd -s" or whatever.
607 */
608 const char *
obp_v0_getbootpath(void)609 obp_v0_getbootpath(void)
610 {
611 struct v0bootargs *ba = promops.po_bootcookie;
612 return (ba->ba_argv[0]);
613 }
614
615 const char *
obp_v0_getbootargs(void)616 obp_v0_getbootargs(void)
617 {
618 struct v0bootargs *ba = promops.po_bootcookie;
619 return (ba->ba_argv[1]);
620 }
621
622 const char *
obp_v0_getbootfile(void)623 obp_v0_getbootfile(void)
624 {
625 struct v0bootargs *ba = promops.po_bootcookie;
626 return (ba->ba_kernel);
627 }
628
629 char *
parse_bootargs(char * args)630 parse_bootargs(char *args)
631 {
632 char *cp;
633
634 for (cp = args; *cp != '\0'; cp++) {
635 if (*cp == '-') {
636 int c;
637 /*
638 * Looks like options start here, but check this
639 * `-' is not part of the kernel name.
640 */
641 if (cp == args)
642 break;
643 if ((c = *(cp-1)) == ' ' || c == '\t')
644 break;
645 }
646 }
647 return (cp);
648 }
649
650 const char *
obp_v2_getbootpath(void)651 obp_v2_getbootpath(void)
652 {
653 struct v2bootargs *ba = promops.po_bootcookie;
654 return (*ba->v2_bootpath);
655 }
656
657 const char *
obp_v2_getbootargs(void)658 obp_v2_getbootargs(void)
659 {
660 struct v2bootargs *ba = promops.po_bootcookie;
661
662 return (parse_bootargs(*ba->v2_bootargs));
663 }
664
665 /*
666 * Static storage shared by prom_getbootfile(), prom_getbootargs() and
667 * prom_getbootpath().
668 * Overwritten on each call!
669 */
670 static char storage[128];
671
672 char *
parse_bootfile(char * args)673 parse_bootfile(char *args)
674 {
675 char *cp, *dp;
676
677 cp = args;
678 dp = storage;
679 while (*cp != 0 && *cp != ' ' && *cp != '\t') {
680 if (dp >= storage + sizeof(storage) - 1) {
681 prom_printf("v2_bootargs too long\n");
682 return (NULL);
683 }
684 if (*cp == '-') {
685 int c;
686 /*
687 * If this `-' is most likely the start of boot
688 * options, we're done.
689 */
690 if (cp == args)
691 break;
692 if ((c = *(cp-1)) == ' ' || c == '\t')
693 break;
694 }
695 *dp++ = *cp++;
696 }
697 *dp = '\0';
698 return (storage);
699 }
700
701 const char *
obp_v2_getbootfile(void)702 obp_v2_getbootfile(void)
703 {
704 struct v2bootargs *ba = promops.po_bootcookie;
705 char *kernel = parse_bootfile(*ba->v2_bootargs);
706 char buf[4+1];
707 const char *prop;
708
709 if (kernel[0] != '\0')
710 return kernel;
711
712 /*
713 * The PROM does not insert the `boot-file' variable if any argument
714 * was given to the `boot' command (e.g `boot -s'). If we determine
715 * in parse_bootfile() above, that boot args contain only switches
716 * then get the `boot-file' value (if any) ourselves.
717 * If the `diag-switch?' PROM variable is set to true, we use
718 * `diag-file' instead.
719 */
720 prop = (prom_getoption("diag-switch?", buf, sizeof buf) != 0 ||
721 strcmp(buf, "true") != 0)
722 ? "diag-file"
723 : "boot-file";
724
725 if (prom_getoption(prop, storage, sizeof storage) != 0)
726 return (NULL);
727
728 return (storage);
729 }
730
731 void
obp_v2_putstr(const char * str,int len)732 obp_v2_putstr(const char *str, int len)
733 {
734 prom_write(prom_stdout(), str, len);
735 }
736
737 void
obp_set_callback(void (* f)(void))738 obp_set_callback(void (*f)(void))
739 {
740 *obpvec->pv_synchook = f;
741 }
742
743 int
obp_ticks(void)744 obp_ticks(void)
745 {
746
747 return (*((int *)promops.po_tickdata));
748 }
749
750 static int
findchosen(void)751 findchosen(void)
752 {
753 static int chosennode;
754 int node;
755
756 if ((node = chosennode) == 0 && (node = OF_finddevice("/chosen")) == -1)
757 panic("no CHOSEN node");
758
759 chosennode = node;
760 return (node);
761 }
762
763 static int
opf_finddevice(const char * name)764 opf_finddevice(const char *name)
765 {
766 int phandle = OF_finddevice(name);
767 if (phandle == -1)
768 return (0);
769 else
770 return (phandle);
771 }
772
773 static int
opf_instance_to_package(int ihandle)774 opf_instance_to_package(int ihandle)
775 {
776 int phandle = OF_instance_to_package(ihandle);
777 if (phandle == -1)
778 return (0);
779 else
780 return (phandle);
781 }
782
783
784 static const char *
opf_getbootpath(void)785 opf_getbootpath(void)
786 {
787 int node = findchosen();
788 char *buf = storage;
789 int blen = sizeof storage;
790
791 if (prom_getprop(node, "bootpath", 1, &blen, &buf) != 0)
792 return ("");
793
794 return (buf);
795 }
796
797 static const char *
opf_getbootargs(void)798 opf_getbootargs(void)
799 {
800 int node = findchosen();
801 char *buf = storage;
802 int blen = sizeof storage;
803
804 if (prom_getprop(node, "bootargs", 1, &blen, &buf) != 0)
805 return ("");
806
807 return (parse_bootargs(buf));
808 }
809
810 static const char *
opf_getbootfile(void)811 opf_getbootfile(void)
812 {
813 int node = findchosen();
814 char *buf = storage;
815 int blen = sizeof storage;
816
817 if (prom_getprop(node, "bootargs", 1, &blen, &buf) != 0)
818 return ("");
819
820 return (parse_bootfile(buf));
821 }
822
823 static char *
opf_nextprop(int node,const char * prop)824 opf_nextprop(int node, const char *prop)
825 {
826 #define OF_NEXTPROP_BUF_SIZE 32 /* specified by the standard */
827 static char buf[OF_NEXTPROP_BUF_SIZE];
828 OF_nextprop(node, prop, buf);
829 return (buf);
830 }
831
832 void
opf_interpret_simple(const char * s)833 opf_interpret_simple(const char *s)
834 {
835 (void)OF_interpret(s, 0, 0);
836 }
837
838 /*
839 * Retrieve physical memory information from the PROM.
840 * If ap is NULL, return the required length of the array.
841 */
842 int
prom_makememarr(struct memarr * ap,int xmax,int which)843 prom_makememarr(struct memarr *ap, int xmax, int which)
844 {
845 struct v0mlist *mp;
846 int node, n;
847 const char *prop;
848
849 if (which != MEMARR_AVAILPHYS && which != MEMARR_TOTALPHYS)
850 panic("makememarr");
851
852 /*
853 * `struct memarr' is in V2 memory property format.
854 * On previous ROM versions we must convert.
855 */
856 switch (prom_version()) {
857 struct promvec *promvec;
858 struct om_vector *oldpvec;
859 case PROM_OLDMON:
860 oldpvec = (struct om_vector *)PROM_BASE;
861 n = 1;
862 if (ap != NULL) {
863 ap[0].zero = 0;
864 ap[0].addr = 0;
865 ap[0].len = (which == MEMARR_AVAILPHYS)
866 ? *oldpvec->memoryAvail
867 : *oldpvec->memorySize;
868 }
869 break;
870
871 case PROM_OBP_V0:
872 /*
873 * Version 0 PROMs use a linked list to describe these
874 * guys.
875 */
876 promvec = romp;
877 mp = (which == MEMARR_AVAILPHYS)
878 ? *promvec->pv_v0mem.v0_physavail
879 : *promvec->pv_v0mem.v0_phystot;
880 for (n = 0; mp != NULL; mp = mp->next, n++) {
881 if (ap == NULL)
882 continue;
883 if (n >= xmax) {
884 printf("makememarr: WARNING: lost some memory\n");
885 break;
886 }
887 ap->zero = 0;
888 ap->addr = (u_long)mp->addr;
889 ap->len = mp->nbytes;
890 ap++;
891 }
892 break;
893
894 default:
895 printf("makememarr: hope version %d PROM is like version 2\n",
896 prom_version());
897 /* FALLTHROUGH */
898
899 case PROM_OBP_V3:
900 case PROM_OBP_V2:
901 /*
902 * Version 2 PROMs use a property array to describe them.
903 */
904
905 /* Consider emulating `OF_finddevice' */
906 node = findnode(firstchild(findroot()), "memory");
907 goto case_common;
908
909 case PROM_OPENFIRM:
910 node = OF_finddevice("/memory");
911 if (node == -1)
912 node = 0;
913
914 case_common:
915 if (node == 0)
916 panic("makememarr: cannot find \"memory\" node");
917
918 prop = (which == MEMARR_AVAILPHYS) ? "available" : "reg";
919 if (ap == NULL) {
920 n = prom_getproplen(node, prop);
921 } else {
922 n = xmax;
923 if (prom_getprop(node, prop, sizeof(struct memarr),
924 &n, &ap) != 0)
925 panic("makememarr: cannot get property");
926 }
927 break;
928 }
929
930 if (n <= 0)
931 panic("makememarr: no memory found");
932 /*
933 * Success! (Hooray)
934 */
935 return (n);
936 }
937
938 static struct idprom idprom;
939 #ifdef _STANDALONE
940 long hostid;
941 #endif
942
943 struct idprom *
prom_getidprom(void)944 prom_getidprom(void)
945 {
946 int node, len;
947 u_long h;
948 u_char *dst;
949
950 if (idprom.idp_format != 0)
951 /* Already got it */
952 return (&idprom);
953
954 dst = (u_char *)&idprom;
955 len = sizeof(struct idprom);
956
957 switch (prom_version()) {
958 case PROM_OLDMON:
959 #ifdef AC_IDPROM
960 {
961 u_char *src = (u_char *)AC_IDPROM;
962 do {
963 *dst++ = lduba(src++, ASI_CONTROL);
964 } while (--len > 0);
965 }
966 #endif
967 break;
968
969 /*
970 * Fetch the `idprom' property at the root node.
971 */
972 case PROM_OBP_V0:
973 case PROM_OBP_V2:
974 case PROM_OPENFIRM:
975 case PROM_OBP_V3:
976 node = prom_findroot();
977 if (prom_getprop(node, "idprom", 1, &len, &dst) != 0) {
978 printf("`idprom' property cannot be read: "
979 "cannot get ethernet address");
980 }
981 break;
982 }
983
984 /* Establish hostid */
985 h = (u_int)idprom.idp_machtype << 24;
986 h |= idprom.idp_serialnum[0] << 16;
987 h |= idprom.idp_serialnum[1] << 8;
988 h |= idprom.idp_serialnum[2];
989 hostid = h;
990
991 return (&idprom);
992 }
993
prom_getether(int node,u_char * cp)994 void prom_getether(int node, u_char *cp)
995 {
996 struct idprom *idp;
997
998 if (prom_get_node_ether(node, cp))
999 return;
1000
1001 /* Fall back on the machine's global ethernet address */
1002 idp = prom_getidprom();
1003 memcpy(cp, idp->idp_etheraddr, 6);
1004 }
1005
1006 bool
prom_get_node_ether(int node,u_char * cp)1007 prom_get_node_ether(int node, u_char *cp)
1008 {
1009 char buf[6+1], *bp;
1010 int nitem;
1011
1012 if (node == 0)
1013 return false;
1014
1015 /*
1016 * First, try the node's "mac-address" property.
1017 * This property is set by the adapter's firmware if the
1018 * device has already been opened for traffic, e.g. for
1019 * net booting. Its value might be `0-terminated', probably
1020 * because the Forth ROMs uses `xdrstring' instead of `xdrbytes'
1021 * to construct the property.
1022 */
1023 nitem = 6+1;
1024 bp = buf;
1025 if (prom_getprop(node, "mac-address", 1, &nitem, &bp) == 0 &&
1026 nitem >= 6) {
1027 memcpy(cp, bp, 6);
1028 return true;
1029 }
1030
1031 /*
1032 * Next, check the global "local-mac-address?" switch to see
1033 * if we should try to extract the node's "local-mac-address"
1034 * property.
1035 */
1036 if (prom_getoption("local-mac-address?", buf, sizeof buf) != 0 ||
1037 strcmp(buf, "true") != 0)
1038 return false;
1039
1040 /* Retrieve the node's "local-mac-address" property, if any */
1041 nitem = 6;
1042 if (prom_getprop(node, "local-mac-address", 1, &nitem, &cp) == 0 &&
1043 nitem == 6)
1044 return true;
1045
1046 return false;
1047 }
1048
1049 /*
1050 * The integer property "get-unum" on the root device is the address
1051 * of a callable function in the PROM that takes a physical address
1052 * (in lo/hipart format) and returns a string identifying the chip
1053 * location of the corresponding memory cell.
1054 */
1055 const char *
prom_pa_location(u_int phys_lo,u_int phys_hi)1056 prom_pa_location(u_int phys_lo, u_int phys_hi)
1057 {
1058 static char *(*unum)(u_int, u_int);
1059 char *str;
1060 const char *unk = "<Unknown>";
1061
1062 switch (prom_version()) {
1063 case PROM_OLDMON:
1064 case PROM_OPENFIRM:
1065 /* to do */
1066 default:
1067 break;
1068 case PROM_OBP_V0:
1069 case PROM_OBP_V2:
1070 case PROM_OBP_V3:
1071 if (unum == NULL)
1072 unum = (char *(*)(u_int,u_int))(u_long)
1073 prom_getpropint(prom_findroot(), "get-unum", 0);
1074
1075 if (unum == NULL || (str = unum(phys_lo, phys_hi)) == NULL)
1076 break;
1077
1078 return (str);
1079 }
1080
1081 return (unk);
1082 }
1083
1084 static void prom_init_oldmon(void);
1085 static void prom_init_obp(void);
1086 static void prom_init_opf(void);
1087
1088 static inline void
prom_init_oldmon(void)1089 prom_init_oldmon(void)
1090 {
1091 struct om_vector *oldpvec = (struct om_vector *)PROM_BASE;
1092 extern void sparc_noop(void);
1093
1094 promops.po_version = PROM_OLDMON;
1095 promops.po_revision = oldpvec->monId[0]; /*XXX*/
1096
1097 promops.po_stdin = *oldpvec->inSource;
1098 promops.po_stdout = *oldpvec->outSink;
1099
1100 promops.po_bootcookie = *oldpvec->bootParam; /* deref 1 lvl */
1101 promops.po_bootpath = obp_v0_getbootpath;
1102 promops.po_bootfile = obp_v0_getbootfile;
1103 promops.po_bootargs = obp_v0_getbootargs;
1104
1105 promops.po_putchar = oldpvec->putChar;
1106 promops.po_getchar = oldpvec->getChar;
1107 promops.po_peekchar = oldpvec->mayGet;
1108 promops.po_putstr = oldpvec->fbWriteStr;
1109 promops.po_reboot = oldpvec->reBoot;
1110 promops.po_abort = oldpvec->abortEntry;
1111 promops.po_halt = oldpvec->exitToMon;
1112 promops.po_ticks = obp_ticks;
1113 promops.po_tickdata = oldpvec->nmiClock;
1114 promops.po_setcallback = (void *)sparc_noop;
1115 promops.po_setcontext = oldpvec->setcxsegmap;
1116
1117 #ifdef SUN4
1118 #ifndef _STANDALONE
1119 if (oldpvec->romvecVersion >= 2) {
1120 extern void oldmon_w_cmd(u_long, char *);
1121 *oldpvec->vector_cmd = oldmon_w_cmd;
1122 }
1123 #endif
1124 #endif
1125 }
1126
1127 static inline void
prom_init_obp(void)1128 prom_init_obp(void)
1129 {
1130 struct nodeops *no;
1131
1132 /*
1133 * OBP v0, v2 & v3
1134 */
1135 switch (obpvec->pv_romvec_vers) {
1136 case 0:
1137 promops.po_version = PROM_OBP_V0;
1138 break;
1139 case 2:
1140 promops.po_version = PROM_OBP_V2;
1141 break;
1142 case 3:
1143 promops.po_version = PROM_OBP_V3;
1144 break;
1145 default:
1146 obpvec->pv_halt(); /* What else? */
1147 }
1148
1149 promops.po_revision = obpvec->pv_printrev;
1150
1151 promops.po_halt = obpvec->pv_halt;
1152 promops.po_reboot = obpvec->pv_reboot;
1153 promops.po_abort = obpvec->pv_abort;
1154 promops.po_setcontext = obpvec->pv_setctxt;
1155 promops.po_setcallback = obp_set_callback;
1156 promops.po_ticks = obp_ticks;
1157 promops.po_tickdata = obpvec->pv_ticks;
1158
1159 /*
1160 * Remove indirection through `pv_nodeops' while we're here.
1161 * Hopefully, the PROM has no need to change this pointer on the fly..
1162 */
1163 no = obpvec->pv_nodeops;
1164 promops.po_firstchild = no->no_child;
1165 promops.po_nextsibling = no->no_nextnode;
1166 promops.po_getproplen = no->no_proplen;
1167 /* XXX - silently discard getprop's `len' argument */
1168 promops.po_getprop = (void *)no->no_getprop;
1169 promops.po_setprop = no->no_setprop;
1170 promops.po_nextprop = no->no_nextprop;
1171
1172 /*
1173 * Next, deal with prom vector differences between versions.
1174 */
1175 switch (promops.po_version) {
1176 case PROM_OBP_V0:
1177 promops.po_stdin = *obpvec->pv_stdin;
1178 promops.po_stdout = *obpvec->pv_stdout;
1179 promops.po_bootcookie = *obpvec->pv_v0bootargs; /* deref 1 lvl */
1180 promops.po_bootpath = obp_v0_getbootpath;
1181 promops.po_bootfile = obp_v0_getbootfile;
1182 promops.po_bootargs = obp_v0_getbootargs;
1183 promops.po_putchar = obpvec->pv_putchar;
1184 promops.po_getchar = obpvec->pv_getchar;
1185 promops.po_peekchar = obpvec->pv_nbgetchar;
1186 promops.po_putstr = obpvec->pv_putstr;
1187 promops.po_open = obpvec->pv_v0devops.v0_open;
1188 promops.po_close = (void *)obpvec->pv_v0devops.v0_close;
1189 promops.po_read = obp_v0_read;
1190 promops.po_write = obp_v0_write;
1191 promops.po_interpret = obp_v0_fortheval;
1192 break;
1193 case PROM_OBP_V3:
1194 promops.po_cpustart = obpvec->pv_v3cpustart;
1195 promops.po_cpustop = obpvec->pv_v3cpustop;
1196 promops.po_cpuidle = obpvec->pv_v3cpuidle;
1197 promops.po_cpuresume = obpvec->pv_v3cpuresume;
1198 /*FALLTHROUGH*/
1199 case PROM_OBP_V2:
1200 /* Deref stdio handles one level */
1201 promops.po_stdin = *obpvec->pv_v2bootargs.v2_fd0;
1202 promops.po_stdout = *obpvec->pv_v2bootargs.v2_fd1;
1203
1204 promops.po_bootcookie = &obpvec->pv_v2bootargs;
1205 promops.po_bootpath = obp_v2_getbootpath;
1206 promops.po_bootfile = obp_v2_getbootfile;
1207 promops.po_bootargs = obp_v2_getbootargs;
1208
1209 promops.po_interpret = obpvec->pv_fortheval.v2_eval;
1210
1211 promops.po_putchar = obp_v2_putchar;
1212 promops.po_getchar = obp_v2_getchar;
1213 promops.po_peekchar = obp_v2_peekchar;
1214 promops.po_putstr = obp_v2_putstr;
1215 promops.po_open = obpvec->pv_v2devops.v2_open;
1216 promops.po_close = (void *)obpvec->pv_v2devops.v2_close;
1217 promops.po_read = obpvec->pv_v2devops.v2_read;
1218 promops.po_write = obpvec->pv_v2devops.v2_write;
1219 promops.po_seek = obp_v2_seek;
1220 promops.po_instance_to_package = obpvec->pv_v2devops.v2_fd_phandle;
1221 promops.po_finddevice = obp_v2_finddevice;
1222
1223 #ifndef _STANDALONE
1224 prom_printf("OBP version %d, revision %d.%d (plugin rev %x)\n",
1225 obpvec->pv_romvec_vers,
1226 obpvec->pv_printrev >> 16, obpvec->pv_printrev & 0xffff,
1227 obpvec->pv_plugin_vers);
1228 #endif
1229 break;
1230 }
1231 }
1232
1233 static inline void
prom_init_opf(void)1234 prom_init_opf(void)
1235 {
1236 int node;
1237
1238 promops.po_version = PROM_OPENFIRM;
1239
1240 /*
1241 * OpenFirmware ops are mostly straightforward.
1242 */
1243 promops.po_halt = OF_exit;
1244 promops.po_reboot = OF_boot;
1245 promops.po_abort = OF_enter;
1246 promops.po_interpret = opf_interpret_simple;
1247 promops.po_setcallback = (void *)OF_set_callback;
1248 promops.po_ticks = OF_milliseconds;
1249
1250 promops.po_bootpath = opf_getbootpath;
1251 promops.po_bootfile = opf_getbootfile;
1252 promops.po_bootargs = opf_getbootargs;
1253
1254 promops.po_firstchild = OF_child;
1255 promops.po_nextsibling = OF_peer;
1256 promops.po_getproplen = OF_getproplen;
1257 promops.po_getprop = OF_getprop;
1258 promops.po_nextprop = opf_nextprop;
1259 promops.po_setprop = OF_setprop;
1260
1261 /* We can re-use OBP v2 emulation */
1262 promops.po_putchar = obp_v2_putchar;
1263 promops.po_getchar = obp_v2_getchar;
1264 promops.po_peekchar = obp_v2_peekchar;
1265 promops.po_putstr = obp_v2_putstr;
1266
1267 promops.po_open = OF_open;
1268 promops.po_close = OF_close;
1269 promops.po_read = OF_read;
1270 promops.po_write = OF_write;
1271 promops.po_seek = OF_seek;
1272 promops.po_instance_to_package = opf_instance_to_package;
1273 promops.po_finddevice = opf_finddevice;
1274
1275 /* Retrieve and cache stdio handles */
1276 node = findchosen();
1277 OF_getprop(node, "stdin", &promops.po_stdin, sizeof(int));
1278 OF_getprop(node, "stdout", &promops.po_stdout, sizeof(int));
1279
1280 OF_init();
1281 }
1282
1283 /*
1284 * Initialize our PROM operations vector.
1285 */
1286 void
prom_init(void)1287 prom_init(void)
1288 {
1289 #ifdef _STANDALONE
1290 int node;
1291 char *cp;
1292 #endif
1293
1294 if (CPU_ISSUN4) {
1295 prom_init_oldmon();
1296 } else if (obpvec->pv_magic == OBP_MAGIC) {
1297 prom_init_obp();
1298 } else {
1299 /*
1300 * Assume this is an Openfirm machine.
1301 */
1302 prom_init_opf();
1303 }
1304
1305 #ifdef _STANDALONE
1306 /*
1307 * Find out what type of machine we're running on.
1308 *
1309 * This process is actually started in srt0.S, which has discovered
1310 * the minimal set of machine specific parameters for the 1st-level
1311 * boot program (bootxx) to run. The page size has already been set
1312 * and the CPU type is either CPU_SUN4, CPU_SUN4C or CPU_SUN4M.
1313 */
1314
1315 if (cputyp == CPU_SUN4 || cputyp == CPU_SUN4M)
1316 return;
1317
1318 /*
1319 * We have SUN4C, SUN4M or SUN4D.
1320 * Use the PROM `compatible' property to determine which.
1321 * Absence of the `compatible' property means `sun4c'.
1322 */
1323
1324 node = prom_findroot();
1325 cp = prom_getpropstring(node, "compatible");
1326 if (*cp == '\0' || strcmp(cp, "sun4c") == 0)
1327 cputyp = CPU_SUN4C;
1328 else if (strcmp(cp, "sun4m") == 0)
1329 cputyp = CPU_SUN4M;
1330 else if (strcmp(cp, "sun4d") == 0)
1331 cputyp = CPU_SUN4D;
1332 else
1333 printf("Unknown CPU type (compatible=`%s')\n", cp);
1334 #endif /* _STANDALONE */
1335 }
1336