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