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