yaboot menu user interface
[yaboot.git] / second / prom.c
1 /*
2  *  prom.c - Routines for talking to the Open Firmware PROM
3  *
4  *  Copyright (C) 2001, 2002 Ethan Benson
5  *
6  *  Copyright (C) 1999 Benjamin Herrenschmidt
7  *
8  *  Copyright (C) 1999 Marius Vollmer
9  *
10  *  Copyright (C) 1996 Paul Mackerras.
11  *
12  *  This program is free software; you can redistribute it and/or modify
13  *  it under the terms of the GNU General Public License as published by
14  *  the Free Software Foundation; either version 2 of the License, or
15  *  (at your option) any later version.
16  *
17  *  This program is distributed in the hope that it will be useful,
18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *  GNU General Public License for more details.
21  *
22  *  You should have received a copy of the GNU General Public License
23  *  along with this program; if not, write to the Free Software
24  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #include "prom.h"
28 #include "stdarg.h"
29 #include "stddef.h"
30 #include "stdlib.h"
31 #include "types.h"
32 #include "ctype.h"
33 #include "asm/processor.h"
34 #include "errors.h"
35 #include "debug.h"
36
37 #define READ_BLOCKS_USE_READ    1
38
39 prom_entry prom;
40
41 ihandle prom_stdin, prom_stdout;
42
43 //extern int vsprintf(char *buf, const char *fmt, va_list args);
44
45 static ihandle prom_mem, prom_mmu;
46 static ihandle prom_chosen, prom_options;
47
48 struct prom_args {
49      const char *service;
50      int nargs;
51      int nret;
52      void *args[10];
53 };
54
55 void *
56 call_prom (const char *service, int nargs, int nret, ...)
57 {
58      va_list list;
59      int i;
60      struct prom_args prom_args;
61
62      prom_args.service = service;
63      prom_args.nargs = nargs;
64      prom_args.nret = nret;
65      va_start (list, nret);
66      for (i = 0; i < nargs; ++i)
67           prom_args.args[i] = va_arg(list, void *);
68      va_end(list);
69      for (i = 0; i < nret; ++i)
70           prom_args.args[i + nargs] = 0;
71      prom (&prom_args);
72      if (nret > 0)
73           return prom_args.args[nargs];
74      else
75           return 0;
76 }
77
78 void *
79 call_prom_return (const char *service, int nargs, int nret, ...)
80 {
81      va_list list;
82      int i;
83      void* result;
84      struct prom_args prom_args;
85
86      prom_args.service = service;
87      prom_args.nargs = nargs;
88      prom_args.nret = nret;
89      va_start (list, nret);
90      for (i = 0; i < nargs; ++i)
91           prom_args.args[i] = va_arg(list, void *);
92      for (i = 0; i < nret; ++i)
93           prom_args.args[i + nargs] = 0;
94      if (prom (&prom_args) != 0)
95           return PROM_INVALID_HANDLE;
96      if (nret > 0) {
97           result = prom_args.args[nargs];
98           for (i=1; i<nret; i++) {
99                void** rp = va_arg(list, void**);
100                *rp = prom_args.args[i+nargs];
101           }
102      } else
103           result = 0;
104      va_end(list);
105      return result;
106 }
107
108 static void *
109 call_method_1 (char *method, prom_handle h, int nargs, ...)
110 {
111      va_list list;
112      int i;
113      struct prom_args prom_args;
114
115      prom_args.service = "call-method";
116      prom_args.nargs = nargs+2;
117      prom_args.nret = 2;
118      prom_args.args[0] = method;
119      prom_args.args[1] = h;
120      va_start (list, nargs);
121      for (i = 0; i < nargs; ++i)
122           prom_args.args[2+i] = va_arg(list, void *);
123      va_end(list);
124      prom_args.args[2+nargs] = 0;
125      prom_args.args[2+nargs+1] = 0;
126
127      prom (&prom_args);
128
129      if (prom_args.args[2+nargs] != 0)
130      {
131           prom_printf ("method '%s' failed %p\n", method, prom_args.args[2+nargs]);
132           return 0;
133      }
134      return prom_args.args[2+nargs+1];
135 }
136
137
138 prom_handle
139 prom_finddevice (char *name)
140 {
141      return call_prom ("finddevice", 1, 1, name);
142 }
143
144 prom_handle
145 prom_findpackage(char *path)
146 {
147      return call_prom ("find-package", 1, 1, path);
148 }
149
150 int
151 prom_getprop (prom_handle pack, char *name, void *mem, int len)
152 {
153      return (int)call_prom ("getprop", 4, 1, pack, name, mem, len);
154 }
155
156 int
157 prom_getproplen(prom_handle pack, const char *name)
158 {
159      return (int)call_prom("getproplen", 2, 1, pack, name);
160 }
161
162 int
163 prom_setprop (prom_handle pack, char *name, void *mem, int len)
164 {
165      return (int)call_prom ("setprop", 4, 1, pack, name, mem, len);
166 }
167
168 int
169 prom_get_chosen (char *name, void *mem, int len)
170 {
171      return prom_getprop (prom_chosen, name, mem, len);
172 }
173
174 int
175 prom_get_options (char *name, void *mem, int len)
176 {
177      if (prom_options == (void *)-1)
178           return -1;
179      return prom_getprop (prom_options, name, mem, len);
180 }
181
182 int
183 prom_set_options (char *name, void *mem, int len)
184 {
185      if (prom_options == (void *)-1)
186           return -1;
187      return prom_setprop (prom_options, name, mem, len);
188 }
189
190 int
191 prom_get_devtype (char *device)
192 {
193      phandle    dev;
194      int        result;
195      char       tmp[64];
196
197      if (strstr(device, TOK_ISCSI))
198           device = strcpy(tmp, "/vdevice/gscsi/disk");
199
200      /* Find OF device phandle */
201      dev = prom_finddevice(device);
202      if (dev == PROM_INVALID_HANDLE) {
203           return FILE_ERR_BADDEV;
204      }
205
206      /* Check the kind of device */
207      result = prom_getprop(dev, "device_type", tmp, 63);
208      if (result == -1) {
209           prom_printf("can't get <device_type> for device: %s\n", device);
210           return FILE_ERR_BADDEV;
211      }
212      tmp[result] = 0;
213      if (!strcmp(tmp, "block"))
214           return FILE_DEVICE_BLOCK;
215      else if (!strcmp(tmp, "network"))
216           return FILE_DEVICE_NET;
217      else {
218           prom_printf("Unkown device type <%s>\n", tmp);
219           return FILE_ERR_BADDEV;
220      }
221 }
222
223 void
224 prom_init (prom_entry pp)
225 {
226      prom = pp;
227
228      prom_chosen = prom_finddevice ("/chosen");
229      if (prom_chosen == (void *)-1)
230           prom_exit ();
231      prom_options = prom_finddevice ("/options");
232      if (prom_get_chosen ("stdout", &prom_stdout, sizeof(prom_stdout)) <= 0)
233           prom_exit();
234      if (prom_get_chosen ("stdin", &prom_stdin, sizeof(prom_stdin)) <= 0)
235           prom_abort ("\nCan't open stdin");
236      if (prom_get_chosen ("memory", &prom_mem, sizeof(prom_mem)) <= 0)
237           prom_abort ("\nCan't get mem handle");
238      if (prom_get_chosen ("mmu", &prom_mmu, sizeof(prom_mmu)) <= 0)
239           prom_abort ("\nCan't get mmu handle");
240
241   // move cursor to fresh line
242      prom_printf ("\n");
243
244      /* Add a few OF methods (thanks Darwin) */
245 #if DEBUG
246      prom_printf ("Adding OF methods...\n");
247 #endif
248
249      prom_interpret (
250           /* All values in this forth code are in hex */
251           "hex "
252           /* Those are a few utilities ripped from Apple */
253           ": D2NIP decode-int nip nip ;\r"      // A useful function to save space
254           ": GPP$ get-package-property 0= ;\r"  // Another useful function to save space
255           ": ^on0 0= if -1 throw then ;\r"      // Bail if result zero
256           ": $CM $call-method ;\r"
257           );
258
259      /* Some forth words used by the release method */
260      prom_interpret (
261           " \" /chosen\" find-package if "
262                  "dup \" memory\" rot GPP$ if "
263                          "D2NIP swap "                           // ( MEMORY-ihandle "/chosen"-phandle )
264                          "\" mmu\" rot GPP$ if "
265                                  "D2NIP "                                // ( MEMORY-ihandle MMU-ihandle )
266                          "else "
267                                  "0 "                                    // ( MEMORY-ihandle 0 )
268                          "then "
269                  "else "
270                          "0 0 "                                          // ( 0 0 )
271                  "then "
272           "else "
273                  "0 0 "                                                  // ( 0 0 )
274           "then\r"
275           "value mmu# "
276           "value mem# "
277           );
278
279      prom_interpret (
280           ": ^mem mem# $CM ; "
281           ": ^mmu mmu# $CM ; "
282           );
283
284      DEBUG_F("OF interface initialized.\n");
285 }
286
287 prom_handle
288 prom_open (char *spec)
289 {
290      return call_prom ("open", 1, 1, spec, strlen(spec));
291 }
292
293 void
294 prom_close (prom_handle file)
295 {
296      call_prom ("close", 1, 0, file);
297 }
298
299 int
300 prom_read (prom_handle file, void *buf, int n)
301 {
302      int result = 0;
303      int retries = 10;
304
305      if (n == 0)
306           return 0;
307      while(--retries) {
308           result = (int)call_prom ("read", 3, 1, file, buf, n);
309           if (result != 0)
310                break;
311           call_prom("interpret", 1, 1, " 10 ms");
312      }
313
314      return result;
315 }
316
317 int
318 prom_write (prom_handle file, void *buf, int n)
319 {
320      return (int)call_prom ("write", 3, 1, file, buf, n);
321 }
322
323 int
324 prom_seek (prom_handle file, int pos)
325 {
326      int status = (int)call_prom ("seek", 3, 1, file, 0, pos);
327      return status == 0 || status == 1;
328 }
329
330 int
331 prom_lseek (prom_handle file, unsigned long long pos)
332 {
333      int status = (int)call_prom ("seek", 3, 1, file,
334                                   (unsigned int)(pos >> 32), (unsigned int)(pos & 0xffffffffUL));
335      return status == 0 || status == 1;
336 }
337
338 int
339 prom_loadmethod (prom_handle device, void* addr)
340 {
341      return (int)call_method_1 ("load", device, 1, addr);
342 }
343
344 int
345 prom_getblksize (prom_handle file)
346 {
347      return (int)call_method_1 ("block-size", file, 0);
348 }
349
350 int
351 prom_readblocks (prom_handle dev, int blockNum, int blockCount, void *buffer)
352 {
353 #if READ_BLOCKS_USE_READ
354      int status;
355      unsigned int blksize;
356
357      blksize = prom_getblksize(dev);
358      if (blksize <= 1)
359           blksize = 512;
360      status = prom_seek(dev, blockNum * blksize);
361      if (status != 1) {
362           return 0;
363           prom_printf("Can't seek to 0x%x\n", blockNum * blksize);
364      }
365
366      status = prom_read(dev, buffer, blockCount * blksize);
367 //  prom_printf("prom_readblocks, bl: %d, cnt: %d, status: %d\n",
368 //      blockNum, blockCount, status);
369
370      return status == (blockCount * blksize);
371 #else
372      int result;
373      int retries = 10;
374
375      if (blockCount == 0)
376           return blockCount;
377      while(--retries) {
378           result = call_method_1 ("read-blocks", dev, 3, buffer, blockNum, blockCount);
379           if (result != 0)
380                break;
381           call_prom("interpret", 1, 1, " 10 ms");
382      }
383
384      return result;
385 #endif
386 }
387
388 int
389 prom_getchar ()
390 {
391      char c[4];
392      int a;
393
394      while ((a = (int)call_prom ("read", 3, 1, prom_stdin, c, 4)) == 0)
395           ;
396      if (a == -1)
397           prom_abort ("EOF on console\n");
398      if (a == 3 && c[0] == '\e' && c[1] == '[')
399           return 0x100 | c[2];
400      return c[0];
401 }
402
403 int
404 prom_nbgetchar()
405 {
406      char ch;
407
408      return (int) call_prom("read", 3, 1, prom_stdin, &ch, 1) > 0? ch: -1;
409 }
410
411 void
412 prom_putchar (char c)
413 {
414      if (c == '\n')
415           call_prom ("write", 3, 1, prom_stdout, "\r\n", 2);
416      else
417           call_prom ("write", 3, 1, prom_stdout, &c, 1);
418 }
419
420 void
421 prom_puts (prom_handle file, char *s)
422 {
423      const char *p, *q;
424
425      for (p = s; *p != 0; p = q)
426      {
427           for (q = p; *q != 0 && *q != '\n'; ++q)
428                ;
429           if (q > p)
430                call_prom ("write", 3, 1, file, p, q - p);
431           if (*q != 0)
432           {
433                ++q;
434                call_prom ("write", 3, 1, file, "\r\n", 2);
435           }
436      }
437 }
438
439 void
440 prom_vfprintf (prom_handle file, char *fmt, va_list ap)
441 {
442      static char printf_buf[2048];
443      vsprintf (printf_buf, fmt, ap);
444      prom_puts (file, printf_buf);
445 }
446
447 void
448 prom_vprintf (char *fmt, va_list ap)
449 {
450      static char printf_buf[2048];
451      vsprintf (printf_buf, fmt, ap);
452      prom_puts (prom_stdout, printf_buf);
453 }
454
455 void
456 prom_fprintf (prom_handle file, char *fmt, ...)
457 {
458      va_list ap;
459      va_start (ap, fmt);
460      prom_vfprintf (file, fmt, ap);
461      va_end (ap);
462 }
463
464 void
465 prom_printf (char *fmt, ...)
466 {
467      va_list ap;
468      va_start (ap, fmt);
469      prom_vfprintf (prom_stdout, fmt, ap);
470      va_end (ap);
471 }
472
473 void
474 prom_perror (int error, char *filename)
475 {
476      if (error == FILE_ERR_EOF)
477           prom_printf("%s: Unexpected End Of File\n", filename);
478      else if (error == FILE_ERR_NOTFOUND)
479           prom_printf("%s: No such file or directory\n", filename);
480      else if (error == FILE_CANT_SEEK)
481           prom_printf("%s: Seek error\n", filename);
482      else if (error == FILE_IOERR)
483           prom_printf("%s: Input/output error\n", filename);
484      else if (error == FILE_BAD_PATH)
485           prom_printf("%s: Path too long\n", filename);
486      else if (error == FILE_ERR_BAD_TYPE)
487           prom_printf("%s: Not a regular file\n", filename);
488      else if (error == FILE_ERR_NOTDIR)
489           prom_printf("%s: Not a directory\n", filename);
490      else if (error == FILE_ERR_BAD_FSYS)
491           prom_printf("%s: Unknown or corrupt filesystem\n", filename);
492      else if (error == FILE_ERR_SYMLINK_LOOP)
493           prom_printf("%s: Too many levels of symbolic links\n", filename);
494      else if (error == FILE_ERR_LENGTH)
495           prom_printf("%s: File too large\n", filename);
496      else if (error == FILE_ERR_FSBUSY)
497           prom_printf("%s: Filesystem busy\n", filename);
498      else if (error == FILE_ERR_BADDEV)
499           prom_printf("%s: Unable to open file, Invalid device\n", filename);
500      else
501           prom_printf("%s: Unknown error\n", filename);
502 }
503
504 void
505 prom_readline (char *prompt, char *buf, int len)
506 {
507      int i = 0;
508      int c;
509
510      if (prompt)
511           prom_puts (prom_stdout, prompt);
512
513      while (i < len-1 && (c = prom_getchar ()) != '\r')
514      {
515           if (c >= 0x100)
516                continue;
517           if (c == 8)
518           {
519                if (i > 0)
520                {
521                     prom_puts (prom_stdout, "\b \b");
522                     i--;
523                }
524                else
525                     prom_putchar ('\a');
526           }
527           else if (isprint (c))
528           {
529                prom_putchar (c);
530                buf[i++] = c;
531           }
532           else
533                prom_putchar ('\a');
534      }
535      prom_putchar ('\n');
536      buf[i] = 0;
537 }
538
539 #ifdef CONFIG_SET_COLORMAP
540 int prom_set_color(prom_handle device, int color, int r, int g, int b)
541 {
542      return (int)call_prom( "call-method", 6, 1, "color!", device, color, b, g, r );
543 }
544 #endif /* CONFIG_SET_COLORMAP */
545
546 void
547 prom_exit ()
548 {
549      call_prom ("exit", 0, 0);
550 }
551
552 void
553 prom_abort (char *fmt, ...)
554 {
555      va_list ap;
556      va_start (ap, fmt);
557      prom_vfprintf (prom_stdout, fmt, ap);
558      va_end (ap);
559      prom_exit ();
560 }
561
562 void
563 prom_sleep (int seconds)
564 {
565      int end;
566      end = (prom_getms() + (seconds * 1000));
567      while (prom_getms() <= end);
568 }
569
570 void *
571 prom_claim (void *virt, unsigned int size, unsigned int align)
572 {
573      return call_prom ("claim", 3, 1, virt, size, align);
574 }
575
576 void
577 prom_release(void *virt, unsigned int size)
578 {
579      call_prom ("release", 2, 0, virt, size);
580 #if 0 /* this is bullshit, newworld OF RELEASE method works fine. */
581
582      /* release in not enough, it needs also an unmap call. This bit of forth
583       * code inspired from Darwin's bootloader but could be replaced by direct
584       * calls to the MMU package if needed
585       */
586      call_prom ("interpret", 3, 1,
587 #if DEBUG
588                 ".\" ReleaseMem:\" 2dup . . cr "
589 #endif
590                 "over \" translate\" ^mmu "             // Find out physical base
591                 "^on0 "                                 // Bail if translation failed
592                 "drop "                                 // Leaving phys on top of stack
593                 "2dup \" unmap\" ^mmu "                 // Unmap the space first
594                 "2dup \" release\" ^mmu "               // Then free the virtual pages
595                 "\" release\" ^mem "                    // Then free the physical pages
596                 ,size, virt
597           );
598 #endif /* bullshit */
599 }
600
601 void
602 prom_map (void *phys, void *virt, int size)
603 {
604      unsigned long msr = mfmsr();
605
606      /* Only create a mapping if we're running with relocation enabled. */
607      if ( (msr & MSR_IR) && (msr & MSR_DR) )
608           call_method_1 ("map", prom_mmu, 4, -1, size, virt, phys);
609 }
610
611 void
612 prom_unmap (void *phys, void *virt, int size)
613 {
614      unsigned long msr = mfmsr();
615
616      /* Only unmap if we're running with relocation enabled. */
617      if ( (msr & MSR_IR) && (msr & MSR_DR) )
618           call_method_1 ("map", prom_mmu, 4, -1, size, virt, phys);
619 }
620
621 char *
622 prom_getargs ()
623 {
624      static char args[256];
625      int l;
626
627      l = prom_get_chosen ("bootargs", args, 255);
628      args[l] = '\0';
629      return args;
630 }
631
632 void
633 prom_setargs (char *args)
634 {
635      int l = strlen (args)+1;
636      if ((int)call_prom ("setprop", 4, 1, prom_chosen, "bootargs", args, l) != l)
637           prom_printf ("can't set args\n");
638 }
639
640 int prom_interpret (char *forth)
641 {
642      return (int)call_prom("interpret", 1, 1, forth);
643 }
644
645 int
646 prom_getms(void)
647 {
648      return (int) call_prom("milliseconds", 0, 1);
649 }
650
651 void
652 prom_pause(void)
653 {
654      call_prom("enter", 0, 0);
655 }
656
657 /*
658  * Local variables:
659  * c-file-style: "k&r"
660  * c-basic-offset: 5
661  * End:
662  */