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