]> git.ozlabs.org Git - yaboot.git/blob - second/prom.c
5aa0a02830f7dbe83d1dae968fca64878c3d1a5a
[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;
391      int a;
392
393      while ((a = (int)call_prom ("read", 3, 1, prom_stdin, &c, 1)) == 0)
394           ;
395      if (a == -1)
396           prom_abort ("EOF on console\n");
397      return c;
398 }
399
400 int
401 prom_nbgetchar()
402 {
403      char ch;
404
405      return (int) call_prom("read", 3, 1, prom_stdin, &ch, 1) > 0? ch: -1;
406 }
407
408 void
409 prom_putchar (char c)
410 {
411      if (c == '\n')
412           call_prom ("write", 3, 1, prom_stdout, "\r\n", 2);
413      else
414           call_prom ("write", 3, 1, prom_stdout, &c, 1);
415 }
416
417 void
418 prom_puts (prom_handle file, char *s)
419 {
420      const char *p, *q;
421
422      for (p = s; *p != 0; p = q)
423      {
424           for (q = p; *q != 0 && *q != '\n'; ++q)
425                ;
426           if (q > p)
427                call_prom ("write", 3, 1, file, p, q - p);
428           if (*q != 0)
429           {
430                ++q;
431                call_prom ("write", 3, 1, file, "\r\n", 2);
432           }
433      }
434 }
435
436 void
437 prom_vfprintf (prom_handle file, char *fmt, va_list ap)
438 {
439      static char printf_buf[2048];
440      vsprintf (printf_buf, fmt, ap);
441      prom_puts (file, printf_buf);
442 }
443
444 void
445 prom_vprintf (char *fmt, va_list ap)
446 {
447      static char printf_buf[2048];
448      vsprintf (printf_buf, fmt, ap);
449      prom_puts (prom_stdout, printf_buf);
450 }
451
452 void
453 prom_fprintf (prom_handle file, char *fmt, ...)
454 {
455      va_list ap;
456      va_start (ap, fmt);
457      prom_vfprintf (file, fmt, ap);
458      va_end (ap);
459 }
460
461 void
462 prom_printf (char *fmt, ...)
463 {
464      va_list ap;
465      va_start (ap, fmt);
466      prom_vfprintf (prom_stdout, fmt, ap);
467      va_end (ap);
468 }
469
470 void
471 prom_perror (int error, char *filename)
472 {
473      if (error == FILE_ERR_EOF)
474           prom_printf("%s: Unexpected End Of File\n", filename);
475      else if (error == FILE_ERR_NOTFOUND)
476           prom_printf("%s: No such file or directory\n", filename);
477      else if (error == FILE_CANT_SEEK)
478           prom_printf("%s: Seek error\n", filename);
479      else if (error == FILE_IOERR)
480           prom_printf("%s: Input/output error\n", filename);
481      else if (error == FILE_BAD_PATH)
482           prom_printf("%s: Path too long\n", filename);
483      else if (error == FILE_ERR_BAD_TYPE)
484           prom_printf("%s: Not a regular file\n", filename);
485      else if (error == FILE_ERR_NOTDIR)
486           prom_printf("%s: Not a directory\n", filename);
487      else if (error == FILE_ERR_BAD_FSYS)
488           prom_printf("%s: Unknown or corrupt filesystem\n", filename);
489      else if (error == FILE_ERR_SYMLINK_LOOP)
490           prom_printf("%s: Too many levels of symbolic links\n", filename);
491      else if (error == FILE_ERR_LENGTH)
492           prom_printf("%s: File too large\n", filename);
493      else if (error == FILE_ERR_FSBUSY)
494           prom_printf("%s: Filesystem busy\n", filename);
495      else if (error == FILE_ERR_BADDEV)
496           prom_printf("%s: Unable to open file, Invalid device\n", filename);
497      else
498           prom_printf("%s: Unknown error\n", filename);
499 }
500
501 void
502 prom_readline (char *prompt, char *buf, int len)
503 {
504      int i = 0;
505      int c;
506
507      if (prompt)
508           prom_puts (prom_stdout, prompt);
509
510      while (i < len-1 && (c = prom_getchar ()) != '\r')
511      {
512           if (c == 8)
513           {
514                if (i > 0)
515                {
516                     prom_puts (prom_stdout, "\b \b");
517                     i--;
518                }
519                else
520                     prom_putchar ('\a');
521           }
522           else if (isprint (c))
523           {
524                prom_putchar (c);
525                buf[i++] = c;
526           }
527           else
528                prom_putchar ('\a');
529      }
530      prom_putchar ('\n');
531      buf[i] = 0;
532 }
533
534 #ifdef CONFIG_SET_COLORMAP
535 int prom_set_color(prom_handle device, int color, int r, int g, int b)
536 {
537      return (int)call_prom( "call-method", 6, 1, "color!", device, color, b, g, r );
538 }
539 #endif /* CONFIG_SET_COLORMAP */
540
541 void
542 prom_exit ()
543 {
544      call_prom ("exit", 0, 0);
545 }
546
547 void
548 prom_abort (char *fmt, ...)
549 {
550      va_list ap;
551      va_start (ap, fmt);
552      prom_vfprintf (prom_stdout, fmt, ap);
553      va_end (ap);
554      prom_exit ();
555 }
556
557 void
558 prom_sleep (int seconds)
559 {
560      int end;
561      end = (prom_getms() + (seconds * 1000));
562      while (prom_getms() <= end);
563 }
564
565 /* if address given is claimed look for other addresses to get the needed
566  * space before giving up
567  */
568 void *
569 prom_claim_chunk(void *virt, unsigned int size, unsigned int align)
570 {
571      void *found, *addr;
572      for(addr=virt; addr <= (void*)PROM_CLAIM_MAX_ADDR;
573          addr+=(0x100000/sizeof(addr))) {
574           found = prom_claim(addr, size, 0);
575           if (found != (void *)-1) {
576                DEBUG_F("claimed %i at 0x%x (0x%x)\n",size,(int)found,(int)virt);
577                return(found);
578           }
579      }
580      prom_printf("Claim error, can't allocate %x at 0x%x\n",size,(int)virt);
581      return((void*)-1);
582 }
583
584 void *
585 prom_claim (void *virt, unsigned int size, unsigned int align)
586 {
587      return call_prom ("claim", 3, 1, virt, size, align);
588 }
589
590 void
591 prom_release(void *virt, unsigned int size)
592 {
593      call_prom ("release", 2, 0, virt, size);
594 }
595
596 void
597 prom_map (void *phys, void *virt, int size)
598 {
599      unsigned long msr = mfmsr();
600
601      /* Only create a mapping if we're running with relocation enabled. */
602      if ( (msr & MSR_IR) && (msr & MSR_DR) )
603           call_method_1 ("map", prom_mmu, 4, -1, size, virt, phys);
604 }
605
606 void
607 prom_unmap (void *phys, void *virt, int size)
608 {
609      unsigned long msr = mfmsr();
610
611      /* Only unmap if we're running with relocation enabled. */
612      if ( (msr & MSR_IR) && (msr & MSR_DR) )
613           call_method_1 ("map", prom_mmu, 4, -1, size, virt, phys);
614 }
615
616 char *
617 prom_getargs ()
618 {
619      static char args[256];
620      int l;
621
622      l = prom_get_chosen ("bootargs", args, 255);
623      args[l] = '\0';
624      return args;
625 }
626
627 void
628 prom_setargs (char *args)
629 {
630      int l = strlen (args)+1;
631      if ((int)call_prom ("setprop", 4, 1, prom_chosen, "bootargs", args, l) != l)
632           prom_printf ("can't set args\n");
633 }
634
635 int prom_interpret (char *forth)
636 {
637      return (int)call_prom("interpret", 1, 1, forth);
638 }
639
640 int
641 prom_getms(void)
642 {
643      return (int) call_prom("milliseconds", 0, 1);
644 }
645
646 void
647 prom_pause(void)
648 {
649      call_prom("enter", 0, 0);
650 }
651
652 /*
653  * prom_get_netinfo()
654  * returns the packet with all needed info for netboot
655  */
656 struct bootp_packet * prom_get_netinfo (void)
657 {
658      void *bootp_response = NULL;
659      char *propname;
660      struct bootp_packet *packet;
661      /* struct bootp_packet contains a VLA, so sizeof won't work.
662         the VLA /must/ be the last field in the structure so use it's
663         offset as a good estimate of the packet size */
664      size_t packet_size = offsetof(struct bootp_packet, options);
665      int i = 0, size, offset = 0;
666      prom_handle chosen;
667 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
668
669      chosen = prom_finddevice("/chosen");
670      if (chosen < 0) {
671           DEBUG_F("chosen=%d\n", chosen);
672       return 0;
673      }
674
675      for (i = 0; i < ARRAY_SIZE(bootp_response_properties); i++) {
676          propname = bootp_response_properties[i].name;
677          size = prom_getproplen(chosen, propname);
678          if (size <= 0)
679              continue;
680
681          DEBUG_F("using /chosen/%s\n", propname);
682          offset = bootp_response_properties[i].offset;
683          break;
684      }
685
686      if (size <= 0)
687          return NULL;
688
689      if (packet_size > size - offset) {
690          prom_printf("Malformed %s property?\n", propname);
691          return NULL;
692      }
693
694      bootp_response = malloc(size);
695      if (!bootp_response)
696          return NULL;
697
698      if (prom_getprop(chosen, propname, bootp_response, size) < 0)
699          return NULL;
700
701      packet = bootp_response + offset;
702      return packet;
703 }
704
705 /*
706  * prom_get_mac()
707  * returns the mac addr of an net card
708  */
709 char * prom_get_mac (struct bootp_packet * packet)
710 {
711      char * conf_path;
712      int i;
713
714      if (!packet)
715         return NULL;
716
717      /* 3 chars per byte in chaddr + \0 */
718      conf_path = malloc(packet->hlen * 3 + 1);
719      if (!conf_path)
720          return NULL;
721      sprintf(conf_path, "%02x", packet->chaddr[0]);
722
723      for (i = 1; i < packet->hlen; i++) {
724       char tmp[4];
725       sprintf(tmp, "-%02x", packet->chaddr[i]);
726       strcat(conf_path, tmp);
727      }
728
729      return conf_path;
730 }
731
732 /*
733  * prom_get_ip()
734  * returns the ip addr of an net card
735  */
736 char * prom_get_ip (struct bootp_packet * packet)
737 {
738      char * conf_path;
739
740      if (!packet)
741         return NULL;
742
743      /* 8 chars in yiaddr + \0 */
744      conf_path = malloc(9);
745      if (!conf_path)
746          return NULL;
747      sprintf(conf_path, "%08x", packet->yiaddr);
748
749      return conf_path;
750 }
751
752 /*
753  * Local variables:
754  * c-file-style: "k&r"
755  * c-basic-offset: 5
756  * End:
757  */