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