2c318f9fc4cf448185285f8888c9f510233d0338
[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 #include "string.h"
37
38 #define READ_BLOCKS_USE_READ    1
39
40 prom_entry prom;
41
42 ihandle prom_stdin, prom_stdout;
43
44 static ihandle prom_mem, prom_mmu;
45 static ihandle prom_chosen, prom_options;
46
47 struct prom_args {
48      const char *service;
49      int nargs;
50      int nret;
51      void *args[10];
52 };
53
54 void *
55 call_prom (const char *service, int nargs, int nret, ...)
56 {
57      va_list list;
58      int i;
59      struct prom_args prom_args;
60
61      prom_args.service = service;
62      prom_args.nargs = nargs;
63      prom_args.nret = nret;
64      va_start (list, nret);
65      for (i = 0; i < nargs; ++i)
66           prom_args.args[i] = va_arg(list, void *);
67      va_end(list);
68      for (i = 0; i < nret; ++i)
69           prom_args.args[i + nargs] = 0;
70      prom (&prom_args);
71      if (nret > 0)
72           return prom_args.args[nargs];
73      else
74           return 0;
75 }
76
77 void *
78 call_prom_return (const char *service, int nargs, int nret, ...)
79 {
80      va_list list;
81      int i;
82      void* result;
83      struct prom_args prom_args;
84
85      prom_args.service = service;
86      prom_args.nargs = nargs;
87      prom_args.nret = nret;
88      va_start (list, nret);
89      for (i = 0; i < nargs; ++i)
90           prom_args.args[i] = va_arg(list, void *);
91      for (i = 0; i < nret; ++i)
92           prom_args.args[i + nargs] = 0;
93      if (prom (&prom_args) != 0)
94           return PROM_INVALID_HANDLE;
95      if (nret > 0) {
96           result = prom_args.args[nargs];
97           for (i=1; i<nret; i++) {
98                void** rp = va_arg(list, void**);
99                *rp = prom_args.args[i+nargs];
100           }
101      } else
102           result = 0;
103      va_end(list);
104      return result;
105 }
106
107 static void *
108 call_method_1 (char *method, prom_handle h, int nargs, ...)
109 {
110      va_list list;
111      int i;
112      struct prom_args prom_args;
113
114      prom_args.service = "call-method";
115      prom_args.nargs = nargs+2;
116      prom_args.nret = 2;
117      prom_args.args[0] = method;
118      prom_args.args[1] = h;
119      va_start (list, nargs);
120      for (i = 0; i < nargs; ++i)
121           prom_args.args[2+i] = va_arg(list, void *);
122      va_end(list);
123      prom_args.args[2+nargs] = 0;
124      prom_args.args[2+nargs+1] = 0;
125
126      prom (&prom_args);
127
128      if (prom_args.args[2+nargs] != 0)
129      {
130           prom_printf ("method '%s' failed %p\n", method, prom_args.args[2+nargs]);
131           return 0;
132      }
133      return prom_args.args[2+nargs+1];
134 }
135
136
137 prom_handle
138 prom_finddevice (char *name)
139 {
140      return call_prom ("finddevice", 1, 1, name);
141 }
142
143 prom_handle
144 prom_findpackage(char *path)
145 {
146      return call_prom ("find-package", 1, 1, path);
147 }
148
149 int
150 prom_getprop (prom_handle pack, char *name, void *mem, int len)
151 {
152      return (int)call_prom ("getprop", 4, 1, pack, name, mem, len);
153 }
154
155 int
156 prom_getproplen(prom_handle pack, const char *name)
157 {
158      return (int)call_prom("getproplen", 2, 1, pack, name);
159 }
160
161 int
162 prom_setprop (prom_handle pack, char *name, void *mem, int len)
163 {
164      return (int)call_prom ("setprop", 4, 1, pack, name, mem, len);
165 }
166
167 int
168 prom_get_chosen (char *name, void *mem, int len)
169 {
170      return prom_getprop (prom_chosen, name, mem, len);
171 }
172
173 int
174 prom_get_options (char *name, void *mem, int len)
175 {
176      if (prom_options == (void *)-1)
177           return -1;
178      return prom_getprop (prom_options, name, mem, len);
179 }
180
181 int
182 prom_set_options (char *name, void *mem, int len)
183 {
184      if (prom_options == (void *)-1)
185           return -1;
186      return prom_setprop (prom_options, name, mem, len);
187 }
188
189 int
190 prom_get_devtype (char *device)
191 {
192      phandle    dev;
193      int        result;
194      char       tmp[64];
195
196      if (strstr(device, TOK_ISCSI))
197           device = strcpy(tmp, "/vdevice/gscsi/disk");
198
199      /* Find OF device phandle */
200      dev = prom_finddevice(device);
201      if (dev == PROM_INVALID_HANDLE) {
202           return FILE_ERR_BADDEV;
203      }
204
205      /* Check the kind of device */
206      result = prom_getprop(dev, "device_type", tmp, 63);
207      if (result == -1) {
208           prom_printf("can't get <device_type> for device: %s\n", device);
209           return FILE_ERR_BADDEV;
210      }
211      tmp[result] = 0;
212      if (!strcmp(tmp, "block"))
213           return FILE_DEVICE_BLOCK;
214      else if (!strcmp(tmp, "network"))
215           return FILE_DEVICE_NET;
216      else {
217           prom_printf("Unkown device type <%s>\n", tmp);
218           return FILE_ERR_BADDEV;
219      }
220 }
221
222 void
223 prom_init (prom_entry pp)
224 {
225      prom = pp;
226
227      prom_chosen = prom_finddevice ("/chosen");
228      if (prom_chosen == (void *)-1)
229           prom_exit ();
230      prom_options = prom_finddevice ("/options");
231      if (prom_get_chosen ("stdout", &prom_stdout, sizeof(prom_stdout)) <= 0)
232           prom_exit();
233      if (prom_get_chosen ("stdin", &prom_stdin, sizeof(prom_stdin)) <= 0)
234           prom_abort ("\nCan't open stdin");
235      if (prom_get_chosen ("memory", &prom_mem, sizeof(prom_mem)) <= 0)
236           prom_abort ("\nCan't get mem handle");
237      if (prom_get_chosen ("mmu", &prom_mmu, sizeof(prom_mmu)) <= 0)
238           prom_abort ("\nCan't get mmu handle");
239
240   // move cursor to fresh line
241      prom_printf ("\n");
242
243      /* Add a few OF methods (thanks Darwin) */
244 #if DEBUG
245      prom_printf ("Adding OF methods...\n");
246 #endif
247
248      prom_interpret (
249           /* All values in this forth code are in hex */
250           "hex "
251           /* Those are a few utilities ripped from Apple */
252           ": D2NIP decode-int nip nip ;\r"      // A useful function to save space
253           ": GPP$ get-package-property 0= ;\r"  // Another useful function to save space
254           ": ^on0 0= if -1 throw then ;\r"      // Bail if result zero
255           ": $CM $call-method ;\r"
256           );
257
258      /* Some forth words used by the release method */
259      prom_interpret (
260           " \" /chosen\" find-package if "
261                  "dup \" memory\" rot GPP$ if "
262                          "D2NIP swap "                           // ( MEMORY-ihandle "/chosen"-phandle )
263                          "\" mmu\" rot GPP$ if "
264                                  "D2NIP "                                // ( MEMORY-ihandle MMU-ihandle )
265                          "else "
266                                  "0 "                                    // ( MEMORY-ihandle 0 )
267                          "then "
268                  "else "
269                          "0 0 "                                          // ( 0 0 )
270                  "then "
271           "else "
272                  "0 0 "                                                  // ( 0 0 )
273           "then\r"
274           "value mmu# "
275           "value mem# "
276           );
277
278      prom_interpret (
279           ": ^mem mem# $CM ; "
280           ": ^mmu mmu# $CM ; "
281           );
282
283      DEBUG_F("OF interface initialized.\n");
284 }
285
286 prom_handle
287 prom_open (char *spec)
288 {
289      return call_prom ("open", 1, 1, spec, strlen(spec));
290 }
291
292 void
293 prom_close (prom_handle file)
294 {
295      call_prom ("close", 1, 0, file);
296 }
297
298 int
299 prom_read (prom_handle file, void *buf, int n)
300 {
301      int result = 0;
302      int retries = 10;
303
304      if (n == 0)
305           return 0;
306      while(--retries) {
307           result = (int)call_prom ("read", 3, 1, file, buf, n);
308           if (result != 0)
309                break;
310           call_prom("interpret", 1, 1, " 10 ms");
311      }
312
313      return result;
314 }
315
316 int
317 prom_write (prom_handle file, void *buf, int n)
318 {
319      return (int)call_prom ("write", 3, 1, file, buf, n);
320 }
321
322 int
323 prom_seek (prom_handle file, int pos)
324 {
325      int status = (int)call_prom ("seek", 3, 1, file, 0, pos);
326      return status == 0 || status == 1;
327 }
328
329 int
330 prom_lseek (prom_handle file, unsigned long long pos)
331 {
332      int status = (int)call_prom ("seek", 3, 1, file,
333                                   (unsigned int)(pos >> 32), (unsigned int)(pos & 0xffffffffUL));
334      return status == 0 || status == 1;
335 }
336
337 int
338 prom_loadmethod (prom_handle device, void* addr)
339 {
340      return (int)call_method_1 ("load", device, 1, addr);
341 }
342
343 int
344 prom_getblksize (prom_handle file)
345 {
346      return (int)call_method_1 ("block-size", file, 0);
347 }
348
349 int
350 prom_readblocks (prom_handle dev, int blockNum, int blockCount, void *buffer)
351 {
352 #if READ_BLOCKS_USE_READ
353      int status;
354      unsigned int blksize;
355
356      blksize = prom_getblksize(dev);
357      if (blksize <= 1)
358           blksize = 512;
359      status = prom_seek(dev, blockNum * blksize);
360      if (status != 1) {
361           return 0;
362           prom_printf("Can't seek to 0x%x\n", blockNum * blksize);
363      }
364
365      status = prom_read(dev, buffer, blockCount * blksize);
366 //  prom_printf("prom_readblocks, bl: %d, cnt: %d, status: %d\n",
367 //      blockNum, blockCount, status);
368
369      return status == (blockCount * blksize);
370 #else
371      int result;
372      int retries = 10;
373
374      if (blockCount == 0)
375           return blockCount;
376      while(--retries) {
377           result = call_method_1 ("read-blocks", dev, 3, buffer, blockNum, blockCount);
378           if (result != 0)
379                break;
380           call_prom("interpret", 1, 1, " 10 ms");
381      }
382
383      return result;
384 #endif
385 }
386
387 int
388 prom_getchar ()
389 {
390      char c[4];
391      int a;
392
393      while ((a = (int)call_prom ("read", 3, 1, prom_stdin, c, 4)) == 0)
394           ;
395      if (a == -1)
396           prom_abort ("EOF on console\n");
397      if (a == 3 && c[0] == '\e' && c[1] == '[')
398           return 0x100 | c[2];
399      return c[0];
400 }
401
402 int
403 prom_nbgetchar()
404 {
405      char ch;
406
407      return (int) call_prom("read", 3, 1, prom_stdin, &ch, 1) > 0? ch: -1;
408 }
409
410 void
411 prom_putchar (char c)
412 {
413      if (c == '\n')
414           call_prom ("write", 3, 1, prom_stdout, "\r\n", 2);
415      else
416           call_prom ("write", 3, 1, prom_stdout, &c, 1);
417 }
418
419 void
420 prom_puts (prom_handle file, char *s)
421 {
422      const char *p, *q;
423
424      for (p = s; *p != 0; p = q)
425      {
426           for (q = p; *q != 0 && *q != '\n'; ++q)
427                ;
428           if (q > p)
429                call_prom ("write", 3, 1, file, p, q - p);
430           if (*q != 0)
431           {
432                ++q;
433                call_prom ("write", 3, 1, file, "\r\n", 2);
434           }
435      }
436 }
437
438 void
439 prom_vfprintf (prom_handle file, char *fmt, va_list ap)
440 {
441      static char printf_buf[2048];
442      vsprintf (printf_buf, fmt, ap);
443      prom_puts (file, printf_buf);
444 }
445
446 void
447 prom_vprintf (char *fmt, va_list ap)
448 {
449      static char printf_buf[2048];
450      vsprintf (printf_buf, fmt, ap);
451      prom_puts (prom_stdout, printf_buf);
452 }
453
454 void
455 prom_fprintf (prom_handle file, char *fmt, ...)
456 {
457      va_list ap;
458      va_start (ap, fmt);
459      prom_vfprintf (file, fmt, ap);
460      va_end (ap);
461 }
462
463 void
464 prom_printf (char *fmt, ...)
465 {
466      va_list ap;
467      va_start (ap, fmt);
468      prom_vfprintf (prom_stdout, fmt, ap);
469      va_end (ap);
470 }
471
472 void
473 prom_perror (int error, char *filename)
474 {
475      if (error == FILE_ERR_EOF)
476           prom_printf("%s: Unexpected End Of File\n", filename);
477      else if (error == FILE_ERR_NOTFOUND)
478           prom_printf("%s: No such file or directory\n", filename);
479      else if (error == FILE_CANT_SEEK)
480           prom_printf("%s: Seek error\n", filename);
481      else if (error == FILE_IOERR)
482           prom_printf("%s: Input/output error\n", filename);
483      else if (error == FILE_BAD_PATH)
484           prom_printf("%s: Path too long\n", filename);
485      else if (error == FILE_ERR_BAD_TYPE)
486           prom_printf("%s: Not a regular file\n", filename);
487      else if (error == FILE_ERR_NOTDIR)
488           prom_printf("%s: Not a directory\n", filename);
489      else if (error == FILE_ERR_BAD_FSYS)
490           prom_printf("%s: Unknown or corrupt filesystem\n", filename);
491      else if (error == FILE_ERR_SYMLINK_LOOP)
492           prom_printf("%s: Too many levels of symbolic links\n", filename);
493      else if (error == FILE_ERR_LENGTH)
494           prom_printf("%s: File too large\n", filename);
495      else if (error == FILE_ERR_FSBUSY)
496           prom_printf("%s: Filesystem busy\n", filename);
497      else if (error == FILE_ERR_BADDEV)
498           prom_printf("%s: Unable to open file, Invalid device\n", filename);
499      else
500           prom_printf("%s: Unknown error\n", filename);
501 }
502
503 void
504 prom_readline (char *prompt, char *buf, int len)
505 {
506      int i = 0;
507      int c;
508
509      if (prompt)
510           prom_puts (prom_stdout, prompt);
511
512      while (i < len-1 && (c = prom_getchar ()) != '\r')
513      {
514           if (c >= 0x100)
515                continue;
516           if (c == 8)
517           {
518                if (i > 0)
519                {
520                     prom_puts (prom_stdout, "\b \b");
521                     i--;
522                }
523                else
524                     prom_putchar ('\a');
525           }
526           else if (isprint (c))
527           {
528                prom_putchar (c);
529                buf[i++] = c;
530           }
531           else
532                prom_putchar ('\a');
533      }
534      prom_putchar ('\n');
535      buf[i] = 0;
536 }
537
538 #ifdef CONFIG_SET_COLORMAP
539 int prom_set_color(prom_handle device, int color, int r, int g, int b)
540 {
541      return (int)call_prom( "call-method", 6, 1, "color!", device, color, b, g, r );
542 }
543 #endif /* CONFIG_SET_COLORMAP */
544
545 void
546 prom_exit ()
547 {
548      call_prom ("exit", 0, 0);
549 }
550
551 void
552 prom_abort (char *fmt, ...)
553 {
554      va_list ap;
555      va_start (ap, fmt);
556      prom_vfprintf (prom_stdout, fmt, ap);
557      va_end (ap);
558      prom_exit ();
559 }
560
561 void
562 prom_sleep (int seconds)
563 {
564      int end;
565      end = (prom_getms() + (seconds * 1000));
566      while (prom_getms() <= end);
567 }
568
569 /* if address given is claimed look for other addresses to get the needed
570  * space before giving up
571  */
572 void *
573 prom_claim_chunk(void *virt, unsigned int size, unsigned int align)
574 {
575      void *found, *addr;
576      for(addr=virt; addr <= (void*)PROM_CLAIM_MAX_ADDR;
577          addr+=(0x100000/sizeof(addr))) {
578           found = prom_claim(addr, size, 0);
579           if (found != (void *)-1) {
580                DEBUG_F("claimed %i at 0x%x (0x%x)\n",size,(int)found,(int)virt);
581                return(found);
582           }
583      }
584      prom_printf("Claim error, can't allocate %x at 0x%x\n",size,(int)virt);
585      return((void*)-1);
586 }
587
588 void *
589 prom_claim (void *virt, unsigned int size, unsigned int align)
590 {
591      return call_prom ("claim", 3, 1, virt, size, align);
592 }
593
594 void
595 prom_release(void *virt, unsigned int size)
596 {
597      call_prom ("release", 2, 0, virt, size);
598 }
599
600 void
601 prom_map (void *phys, void *virt, int size)
602 {
603      unsigned long msr = mfmsr();
604
605      /* Only create a mapping if we're running with relocation enabled. */
606      if ( (msr & MSR_IR) && (msr & MSR_DR) )
607           call_method_1 ("map", prom_mmu, 4, -1, size, virt, phys);
608 }
609
610 void
611 prom_unmap (void *phys, void *virt, int size)
612 {
613      unsigned long msr = mfmsr();
614
615      /* Only unmap if we're running with relocation enabled. */
616      if ( (msr & MSR_IR) && (msr & MSR_DR) )
617           call_method_1 ("map", prom_mmu, 4, -1, size, virt, phys);
618 }
619
620 char *
621 prom_getargs ()
622 {
623      static char args[256];
624      int l;
625
626      l = prom_get_chosen ("bootargs", args, 255);
627      args[l] = '\0';
628      return args;
629 }
630
631 void
632 prom_setargs (char *args)
633 {
634      int l = strlen (args)+1;
635      if ((int)call_prom ("setprop", 4, 1, prom_chosen, "bootargs", args, l) != l)
636           prom_printf ("can't set args\n");
637 }
638
639 int prom_interpret (char *forth)
640 {
641      return (int)call_prom("interpret", 1, 1, forth);
642 }
643
644 int
645 prom_getms(void)
646 {
647      return (int) call_prom("milliseconds", 0, 1);
648 }
649
650 void
651 prom_pause(void)
652 {
653      call_prom("enter", 0, 0);
654 }
655
656 /*
657  * prom_get_netinfo()
658  * returns the packet with all needed info for netboot
659  */
660 struct bootp_packet * prom_get_netinfo (void)
661 {
662      void *bootp_response = NULL;
663      char *propname;
664      struct bootp_packet *packet;
665      /* struct bootp_packet contains a VLA, so sizeof won't work.
666         the VLA /must/ be the last field in the structure so use it's
667         offset as a good estimate of the packet size */
668      size_t packet_size = offsetof(struct bootp_packet, options);
669      int i = 0, size, offset = 0;
670      prom_handle chosen;
671 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
672
673      chosen = prom_finddevice("/chosen");
674      if (chosen < 0) {
675           DEBUG_F("chosen=%d\n", chosen);
676       return 0;
677      }
678
679      for (i = 0; i < ARRAY_SIZE(bootp_response_properties); i++) {
680          propname = bootp_response_properties[i].name;
681          size = prom_getproplen(chosen, propname);
682          if (size <= 0)
683              continue;
684
685          DEBUG_F("using /chosen/%s\n", propname);
686          offset = bootp_response_properties[i].offset;
687          break;
688      }
689
690      if (size <= 0)
691          return NULL;
692
693      if (packet_size > size - offset) {
694          prom_printf("Malformed %s property?\n", propname);
695          return NULL;
696      }
697
698      bootp_response = malloc(size);
699      if (!bootp_response)
700          return NULL;
701
702      if (prom_getprop(chosen, propname, bootp_response, size) < 0)
703          return NULL;
704
705      packet = bootp_response + offset;
706      return packet;
707 }
708
709 /*
710  * prom_get_mac()
711  * returns the mac addr of an net card
712  */
713 char * prom_get_mac (struct bootp_packet * packet)
714 {
715      char * conf_path;
716      int i;
717
718      if (!packet)
719         return NULL;
720
721      /* 3 chars per byte in chaddr + \0 */
722      conf_path = malloc(packet->hlen * 3 + 1);
723      if (!conf_path)
724          return NULL;
725      sprintf(conf_path, "%02x", packet->chaddr[0]);
726
727      for (i = 1; i < packet->hlen; i++) {
728       char tmp[4];
729       sprintf(tmp, "-%02x", packet->chaddr[i]);
730       strcat(conf_path, tmp);
731      }
732
733      return conf_path;
734 }
735
736 /*
737  * prom_get_ip()
738  * returns the ip addr of an net card
739  */
740 char * prom_get_ip (struct bootp_packet * packet)
741 {
742      char * conf_path;
743
744      if (!packet)
745         return NULL;
746
747      /* 8 chars in yiaddr + \0 */
748      conf_path = malloc(9);
749      if (!conf_path)
750          return NULL;
751      sprintf(conf_path, "%08x", packet->yiaddr);
752
753      return conf_path;
754 }
755
756 /*
757  * Local variables:
758  * c-file-style: "k&r"
759  * c-basic-offset: 5
760  * End:
761  */