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