xref: /illumos-gate/usr/src/grub/grub-0.97/stage2/builtins.c (revision e0731422366620894c16c1ee6515551c5f00733d)
1 /* builtins.c - the GRUB builtin commands */
2 /*
3  *  GRUB  --  GRand Unified Bootloader
4  *  Copyright (C) 1999,2000,2001,2002,2003,2004  Free Software Foundation, Inc.
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20 
21 /* Include stdio.h before shared.h, because we can't define
22    WITHOUT_LIBC_STUBS here.  */
23 #ifdef GRUB_UTIL
24 # include <stdio.h>
25 #endif
26 
27 #include <shared.h>
28 #include <filesys.h>
29 #include <term.h>
30 
31 #ifdef SUPPORT_NETBOOT
32 # include <grub.h>
33 #endif
34 
35 #ifdef SUPPORT_SERIAL
36 # include <serial.h>
37 # include <terminfo.h>
38 #endif
39 
40 #ifdef GRUB_UTIL
41 # include <device.h>
42 #else /* ! GRUB_UTIL */
43 # include <apic.h>
44 # include <smp-imps.h>
45 #endif /* ! GRUB_UTIL */
46 
47 #ifdef USE_MD5_PASSWORDS
48 # include <md5.h>
49 #endif
50 
51 #include <cpu.h>
52 
53 /* The type of kernel loaded.  */
54 kernel_t kernel_type;
55 /* The boot device.  */
56 static int bootdev;
57 /* True when the debug mode is turned on, and false
58    when it is turned off.  */
59 int debug = 0;
60 /* The default entry.  */
61 int default_entry = 0;
62 /* The fallback entry.  */
63 int fallback_entryno;
64 int fallback_entries[MAX_FALLBACK_ENTRIES];
65 /* The number of current entry.  */
66 int current_entryno;
67 /* The address for Multiboot command-line buffer.  */
68 static char *mb_cmdline;
69 /* The password.  */
70 char *password;
71 /* The password type.  */
72 password_t password_type;
73 /* The flag for indicating that the user is authoritative.  */
74 int auth = 0;
75 /* The timeout.  */
76 int grub_timeout = -1;
77 /* Whether to show the menu or not.  */
78 int show_menu = 1;
79 /* The BIOS drive map.  */
80 static unsigned short bios_drive_map[DRIVE_MAP_SIZE + 1];
81 
82 /* Prototypes for allowing straightfoward calling of builtins functions
83    inside other functions.  */
84 static int configfile_func (char *arg, int flags);
85 #ifdef SUPPORT_NETBOOT
86 static void solaris_config_file (void);
87 #endif
88 
89 static unsigned int min_mem64 = 0;
90 
91 #if defined(__sun) && !defined(GRUB_UTIL)
92 extern void __enable_execute_stack (void *);
93 void
94 __enable_execute_stack (void *addr)
95 {
96 }
97 #endif /* __sun && !GRUB_UTIL */
98 
99 /* Initialize the data for builtins.  */
100 void
101 init_builtins (void)
102 {
103   kernel_type = KERNEL_TYPE_NONE;
104   /* BSD and chainloading evil hacks!  */
105   bootdev = set_bootdev (0);
106   mb_cmdline = (char *) MB_CMDLINE_BUF;
107 }
108 
109 /* Initialize the data for the configuration file.  */
110 void
111 init_config (void)
112 {
113   default_entry = 0;
114   password = 0;
115   fallback_entryno = -1;
116   fallback_entries[0] = -1;
117   grub_timeout = -1;
118   current_rootpool[0] = '\0';
119   current_bootfs[0] = '\0';
120   current_bootpath[0] = '\0';
121   current_bootfs_obj = 0;
122   current_devid[0] = '\0';
123   is_zfs_mount = 0;
124 }
125 
126 /* Check a password for correctness.  Returns 0 if password was
127    correct, and a value != 0 for error, similarly to strcmp. */
128 int
129 check_password (char *entered, char* expected, password_t type)
130 {
131   switch (type)
132     {
133     case PASSWORD_PLAIN:
134       return strcmp (entered, expected);
135 
136 #ifdef USE_MD5_PASSWORDS
137     case PASSWORD_MD5:
138       return check_md5_password (entered, expected);
139 #endif
140     default:
141       /* unsupported password type: be secure */
142       return 1;
143     }
144 }
145 
146 /* Print which sector is read when loading a file.  */
147 static void
148 disk_read_print_func(unsigned int sector, int offset, int length)
149 {
150   grub_printf ("[%u,%d,%d]", sector, offset, length);
151 }
152 
153 
154 /* blocklist */
155 static int
156 blocklist_func (char *arg, int flags)
157 {
158   char *dummy = (char *) RAW_ADDR (0x100000);
159   unsigned int start_sector = 0;
160   int num_sectors = 0;
161   int num_entries = 0;
162   int last_length = 0;
163 
164   auto void disk_read_blocklist_func (unsigned int sector, int offset,
165       int length);
166 
167   /* Collect contiguous blocks into one entry as many as possible,
168      and print the blocklist notation on the screen.  */
169   auto void disk_read_blocklist_func (unsigned int sector, int offset,
170       int length)
171     {
172       if (num_sectors > 0)
173 	{
174 	  if (start_sector + num_sectors == sector
175 	      && offset == 0 && last_length == SECTOR_SIZE)
176 	    {
177 	      num_sectors++;
178 	      last_length = length;
179 	      return;
180 	    }
181 	  else
182 	    {
183 	      if (last_length == SECTOR_SIZE)
184 		grub_printf ("%s%d+%d", num_entries ? "," : "",
185 			     start_sector - part_start, num_sectors);
186 	      else if (num_sectors > 1)
187 		grub_printf ("%s%d+%d,%d[0-%d]", num_entries ? "," : "",
188 			     start_sector - part_start, num_sectors-1,
189 			     start_sector + num_sectors-1 - part_start,
190 			     last_length);
191 	      else
192 		grub_printf ("%s%d[0-%d]", num_entries ? "," : "",
193 			     start_sector - part_start, last_length);
194 	      num_entries++;
195 	      num_sectors = 0;
196 	    }
197 	}
198 
199       if (offset > 0)
200 	{
201 	  grub_printf("%s%u[%d-%d]", num_entries ? "," : "",
202 		      sector-part_start, offset, offset+length);
203 	  num_entries++;
204 	}
205       else
206 	{
207 	  start_sector = sector;
208 	  num_sectors = 1;
209 	  last_length = length;
210 	}
211     }
212 
213   /* Open the file.  */
214   if (! grub_open (arg))
215     return 1;
216 
217   /* Print the device name.  */
218   grub_printf ("(%cd%d",
219 	       (current_drive & 0x80) ? 'h' : 'f',
220 	       current_drive & ~0x80);
221 
222   if ((current_partition & 0xFF0000) != 0xFF0000)
223     grub_printf (",%d", (current_partition >> 16) & 0xFF);
224 
225   if ((current_partition & 0x00FF00) != 0x00FF00)
226     grub_printf (",%c", 'a' + ((current_partition >> 8) & 0xFF));
227 
228   grub_printf (")");
229 
230   /* Read in the whole file to DUMMY.  */
231   disk_read_hook = disk_read_blocklist_func;
232   if (! grub_read (dummy, -1))
233     goto fail;
234 
235   /* The last entry may not be printed yet.  Don't check if it is a
236    * full sector, since it doesn't matter if we read too much. */
237   if (num_sectors > 0)
238     grub_printf ("%s%d+%d", num_entries ? "," : "",
239 		 start_sector - part_start, num_sectors);
240 
241   grub_printf ("\n");
242 
243  fail:
244   disk_read_hook = 0;
245   grub_close ();
246   return errnum;
247 }
248 
249 static struct builtin builtin_blocklist =
250 {
251   "blocklist",
252   blocklist_func,
253   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
254   "blocklist FILE",
255   "Print the blocklist notation of the file FILE."
256 };
257 
258 /* boot */
259 static int
260 boot_func (char *arg, int flags)
261 {
262   /* Clear the int15 handler if we can boot the kernel successfully.
263      This assumes that the boot code never fails only if KERNEL_TYPE is
264      not KERNEL_TYPE_NONE. Is this assumption is bad?  */
265   if (kernel_type != KERNEL_TYPE_NONE)
266     unset_int15_handler ();
267 
268 #ifdef SUPPORT_NETBOOT
269   /* Shut down the networking.  */
270   cleanup_net ();
271 #endif
272 
273   switch (kernel_type)
274     {
275     case KERNEL_TYPE_FREEBSD:
276     case KERNEL_TYPE_NETBSD:
277       /* *BSD */
278       bsd_boot (kernel_type, bootdev, (char *) mbi.cmdline);
279       break;
280 
281     case KERNEL_TYPE_LINUX:
282       /* Linux */
283       linux_boot ();
284       break;
285 
286     case KERNEL_TYPE_BIG_LINUX:
287       /* Big Linux */
288       big_linux_boot ();
289       break;
290 
291     case KERNEL_TYPE_CHAINLOADER:
292       /* Chainloader */
293 
294       /* Check if we should set the int13 handler.  */
295       if (bios_drive_map[0] != 0)
296 	{
297 	  int i;
298 
299 	  /* Search for SAVED_DRIVE.  */
300 	  for (i = 0; i < DRIVE_MAP_SIZE; i++)
301 	    {
302 	      if (! bios_drive_map[i])
303 		break;
304 	      else if ((bios_drive_map[i] & 0xFF) == saved_drive)
305 		{
306 		  /* Exchage SAVED_DRIVE with the mapped drive.  */
307 		  saved_drive = (bios_drive_map[i] >> 8) & 0xFF;
308 		  break;
309 		}
310 	    }
311 
312 	  /* Set the handler. This is somewhat dangerous.  */
313 	  set_int13_handler (bios_drive_map);
314 	}
315 
316       gateA20 (0);
317       boot_drive = saved_drive;
318       chain_stage1 (0, BOOTSEC_LOCATION, boot_part_addr);
319       break;
320 
321     case KERNEL_TYPE_MULTIBOOT:
322       /* Multiboot */
323 #ifdef SUPPORT_NETBOOT
324 #ifdef SOLARIS_NETBOOT
325       if (current_drive == NETWORK_DRIVE) {
326     	/*
327 	 *  XXX Solaris hack: use drive_info to pass network information
328 	 *  Turn off the flag bit to the loader is technically
329 	 *  multiboot compliant.
330 	 */
331     	mbi.flags &= ~MB_INFO_DRIVE_INFO;
332   	mbi.drives_length = dhcpack_length;
333   	mbi.drives_addr = dhcpack_buf;
334       }
335 #endif /* SOLARIS_NETBOOT */
336 #endif
337       multi_boot ((int) entry_addr, (int) &mbi);
338       break;
339 
340     default:
341       errnum = ERR_BOOT_COMMAND;
342       return 1;
343     }
344 
345   return 0;
346 }
347 
348 static struct builtin builtin_boot =
349 {
350   "boot",
351   boot_func,
352   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
353   "boot",
354   "Boot the OS/chain-loader which has been loaded."
355 };
356 
357 
358 #ifdef SUPPORT_NETBOOT
359 /* bootp */
360 static int
361 bootp_func (char *arg, int flags)
362 {
363   int with_configfile = 0;
364 
365   if (grub_memcmp (arg, "--with-configfile", sizeof ("--with-configfile") - 1)
366       == 0)
367     {
368       with_configfile = 1;
369       arg = skip_to (0, arg);
370     }
371 
372   if (! bootp ())
373     {
374       if (errnum == ERR_NONE)
375 	errnum = ERR_DEV_VALUES;
376 
377       return 1;
378     }
379 
380   /* Notify the configuration.  */
381   print_network_configuration ();
382 
383   /* XXX: this can cause an endless loop, but there is no easy way to
384      detect such a loop unfortunately.  */
385   if (with_configfile)
386     configfile_func (config_file, flags);
387 
388   return 0;
389 }
390 
391 static struct builtin builtin_bootp =
392 {
393   "bootp",
394   bootp_func,
395   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
396   "bootp [--with-configfile]",
397   "Initialize a network device via BOOTP. If the option `--with-configfile'"
398   " is given, try to load a configuration file specified by the 150 vendor"
399   " tag."
400 };
401 #endif /* SUPPORT_NETBOOT */
402 
403 
404 /* cat */
405 static int
406 cat_func (char *arg, int flags)
407 {
408   char c;
409 
410   if (! grub_open (arg))
411     return 1;
412 
413   while (grub_read (&c, 1))
414     {
415       /* Because running "cat" with a binary file can confuse the terminal,
416 	 print only some characters as they are.  */
417       if (grub_isspace (c) || (c >= ' ' && c <= '~'))
418 	grub_putchar (c);
419       else
420 	grub_putchar ('?');
421     }
422 
423   grub_close ();
424   return 0;
425 }
426 
427 static struct builtin builtin_cat =
428 {
429   "cat",
430   cat_func,
431   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
432   "cat FILE",
433   "Print the contents of the file FILE."
434 };
435 
436 
437 /* chainloader */
438 static int
439 chainloader_func (char *arg, int flags)
440 {
441   int force = 0;
442   char *file = arg;
443 
444   /* If the option `--force' is specified?  */
445   if (substring ("--force", arg) <= 0)
446     {
447       force = 1;
448       file = skip_to (0, arg);
449     }
450 
451   /* Open the file.  */
452   if (! grub_open (file))
453     {
454       kernel_type = KERNEL_TYPE_NONE;
455       return 1;
456     }
457 
458   /* Read the first block.  */
459   if (grub_read ((char *) BOOTSEC_LOCATION, SECTOR_SIZE) != SECTOR_SIZE)
460     {
461       grub_close ();
462       kernel_type = KERNEL_TYPE_NONE;
463 
464       /* This below happens, if a file whose size is less than 512 bytes
465 	 is loaded.  */
466       if (errnum == ERR_NONE)
467 	errnum = ERR_EXEC_FORMAT;
468 
469       return 1;
470     }
471 
472   /* If not loading it forcibly, check for the signature.  */
473   if (! force
474       && (*((unsigned short *) (BOOTSEC_LOCATION + BOOTSEC_SIG_OFFSET))
475 	  != BOOTSEC_SIGNATURE))
476     {
477       grub_close ();
478       errnum = ERR_EXEC_FORMAT;
479       kernel_type = KERNEL_TYPE_NONE;
480       return 1;
481     }
482 
483   grub_close ();
484   kernel_type = KERNEL_TYPE_CHAINLOADER;
485 
486   /* XXX: Windows evil hack. For now, only the first five letters are
487      checked.  */
488   if (IS_PC_SLICE_TYPE_FAT (current_slice)
489       && ! grub_memcmp ((char *) BOOTSEC_LOCATION + BOOTSEC_BPB_SYSTEM_ID,
490 			"MSWIN", 5))
491     *((unsigned long *) (BOOTSEC_LOCATION + BOOTSEC_BPB_HIDDEN_SECTORS))
492       = part_start;
493 
494   errnum = ERR_NONE;
495 
496   return 0;
497 }
498 
499 static struct builtin builtin_chainloader =
500 {
501   "chainloader",
502   chainloader_func,
503   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
504   "chainloader [--force] FILE",
505   "Load the chain-loader FILE. If --force is specified, then load it"
506   " forcibly, whether the boot loader signature is present or not."
507 };
508 
509 
510 /* This function could be used to debug new filesystem code. Put a file
511    in the new filesystem and the same file in a well-tested filesystem.
512    Then, run "cmp" with the files. If no output is obtained, probably
513    the code is good, otherwise investigate what's wrong...  */
514 /* cmp FILE1 FILE2 */
515 static int
516 cmp_func (char *arg, int flags)
517 {
518   /* The filenames.  */
519   char *file1, *file2;
520   /* The addresses.  */
521   char *addr1, *addr2;
522   int i;
523   /* The size of the file.  */
524   int size;
525 
526   /* Get the filenames from ARG.  */
527   file1 = arg;
528   file2 = skip_to (0, arg);
529   if (! *file1 || ! *file2)
530     {
531       errnum = ERR_BAD_ARGUMENT;
532       return 1;
533     }
534 
535   /* Terminate the filenames for convenience.  */
536   nul_terminate (file1);
537   nul_terminate (file2);
538 
539   /* Read the whole data from FILE1.  */
540   addr1 = (char *) RAW_ADDR (0x100000);
541   if (! grub_open (file1))
542     return 1;
543 
544   /* Get the size.  */
545   size = filemax;
546   if (grub_read (addr1, -1) != size)
547     {
548       grub_close ();
549       return 1;
550     }
551 
552   grub_close ();
553 
554   /* Read the whole data from FILE2.  */
555   addr2 = addr1 + size;
556   if (! grub_open (file2))
557     return 1;
558 
559   /* Check if the size of FILE2 is equal to the one of FILE2.  */
560   if (size != filemax)
561     {
562       grub_printf ("Differ in size: 0x%x [%s], 0x%x [%s]\n",
563 		   size, file1, filemax, file2);
564       grub_close ();
565       return 0;
566     }
567 
568   if (! grub_read (addr2, -1))
569     {
570       grub_close ();
571       return 1;
572     }
573 
574   grub_close ();
575 
576   /* Now compare ADDR1 with ADDR2.  */
577   for (i = 0; i < size; i++)
578     {
579       if (addr1[i] != addr2[i])
580 	grub_printf ("Differ at the offset %d: 0x%x [%s], 0x%x [%s]\n",
581 		     i, (unsigned) addr1[i], file1,
582 		     (unsigned) addr2[i], file2);
583     }
584 
585   return 0;
586 }
587 
588 static struct builtin builtin_cmp =
589 {
590   "cmp",
591   cmp_func,
592   BUILTIN_CMDLINE,
593   "cmp FILE1 FILE2",
594   "Compare the file FILE1 with the FILE2 and inform the different values"
595   " if any."
596 };
597 
598 
599 /* color */
600 /* Set new colors used for the menu interface. Support two methods to
601    specify a color name: a direct integer representation and a symbolic
602    color name. An example of the latter is "blink-light-gray/blue".  */
603 static int
604 color_func (char *arg, int flags)
605 {
606   char *normal;
607   char *highlight;
608   int new_normal_color;
609   int new_highlight_color;
610   static char *color_list[16] =
611   {
612     "black",
613     "blue",
614     "green",
615     "cyan",
616     "red",
617     "magenta",
618     "brown",
619     "light-gray",
620     "dark-gray",
621     "light-blue",
622     "light-green",
623     "light-cyan",
624     "light-red",
625     "light-magenta",
626     "yellow",
627     "white"
628   };
629 
630   auto int color_number (char *str);
631 
632   /* Convert the color name STR into the magical number.  */
633   auto int color_number (char *str)
634     {
635       char *ptr;
636       int i;
637       int color = 0;
638 
639       /* Find the separator.  */
640       for (ptr = str; *ptr && *ptr != '/'; ptr++)
641 	;
642 
643       /* If not found, return -1.  */
644       if (! *ptr)
645 	return -1;
646 
647       /* Terminate the string STR.  */
648       *ptr++ = 0;
649 
650       /* If STR contains the prefix "blink-", then set the `blink' bit
651 	 in COLOR.  */
652       if (substring ("blink-", str) <= 0)
653 	{
654 	  color = 0x80;
655 	  str += 6;
656 	}
657 
658       /* Search for the color name.  */
659       for (i = 0; i < 16; i++)
660 	if (grub_strcmp (color_list[i], str) == 0)
661 	  {
662 	    color |= i;
663 	    break;
664 	  }
665 
666       if (i == 16)
667 	return -1;
668 
669       str = ptr;
670       nul_terminate (str);
671 
672       /* Search for the color name.  */
673       for (i = 0; i < 8; i++)
674 	if (grub_strcmp (color_list[i], str) == 0)
675 	  {
676 	    color |= i << 4;
677 	    break;
678 	  }
679 
680       if (i == 8)
681 	return -1;
682 
683       return color;
684     }
685 
686   normal = arg;
687   highlight = skip_to (0, arg);
688 
689   new_normal_color = color_number (normal);
690   if (new_normal_color < 0 && ! safe_parse_maxint (&normal, &new_normal_color))
691     return 1;
692 
693   /* The second argument is optional, so set highlight_color
694      to inverted NORMAL_COLOR.  */
695   if (! *highlight)
696     new_highlight_color = ((new_normal_color >> 4)
697 			   | ((new_normal_color & 0xf) << 4));
698   else
699     {
700       new_highlight_color = color_number (highlight);
701       if (new_highlight_color < 0
702 	  && ! safe_parse_maxint (&highlight, &new_highlight_color))
703 	return 1;
704     }
705 
706   if (current_term->setcolor)
707     current_term->setcolor (new_normal_color, new_highlight_color);
708 
709   return 0;
710 }
711 
712 static struct builtin builtin_color =
713 {
714   "color",
715   color_func,
716   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
717   "color NORMAL [HIGHLIGHT]",
718   "Change the menu colors. The color NORMAL is used for most"
719   " lines in the menu, and the color HIGHLIGHT is used to highlight the"
720   " line where the cursor points. If you omit HIGHLIGHT, then the"
721   " inverted color of NORMAL is used for the highlighted line."
722   " The format of a color is \"FG/BG\". FG and BG are symbolic color names."
723   " A symbolic color name must be one of these: black, blue, green,"
724   " cyan, red, magenta, brown, light-gray, dark-gray, light-blue,"
725   " light-green, light-cyan, light-red, light-magenta, yellow and white."
726   " But only the first eight names can be used for BG. You can prefix"
727   " \"blink-\" to FG if you want a blinking foreground color."
728 };
729 
730 
731 /* configfile */
732 static int
733 configfile_func (char *arg, int flags)
734 {
735   char *new_config = config_file;
736 
737   /* Check if the file ARG is present.  */
738   if (! grub_open (arg))
739     return 1;
740 
741   grub_close ();
742 
743   /* Copy ARG to CONFIG_FILE.  */
744   while ((*new_config++ = *arg++) != 0)
745     ;
746 
747 #ifdef GRUB_UTIL
748   /* Force to load the configuration file.  */
749   use_config_file = 1;
750 #endif
751 
752   /* Make sure that the user will not be authoritative.  */
753   auth = 0;
754 
755   /* Restart cmain.  */
756   grub_longjmp (restart_env, 0);
757 
758   /* Never reach here.  */
759   return 0;
760 }
761 
762 static struct builtin builtin_configfile =
763 {
764   "configfile",
765   configfile_func,
766   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
767   "configfile FILE",
768   "Load FILE as the configuration file."
769 };
770 
771 
772 /* debug */
773 static int
774 debug_func (char *arg, int flags)
775 {
776   if (debug)
777     {
778       debug = 0;
779       grub_printf (" Debug mode is turned off\n");
780     }
781   else
782     {
783       debug = 1;
784       grub_printf (" Debug mode is turned on\n");
785     }
786 
787   return 0;
788 }
789 
790 static struct builtin builtin_debug =
791 {
792   "debug",
793   debug_func,
794   BUILTIN_CMDLINE,
795   "debug",
796   "Turn on/off the debug mode."
797 };
798 
799 
800 /* default */
801 static int
802 default_func (char *arg, int flags)
803 {
804 #ifndef SUPPORT_DISKLESS
805   if (grub_strcmp (arg, "saved") == 0)
806     {
807       default_entry = saved_entryno;
808       return 0;
809     }
810 #endif /* SUPPORT_DISKLESS */
811 
812   if (! safe_parse_maxint (&arg, &default_entry))
813     return 1;
814 
815   return 0;
816 }
817 
818 static struct builtin builtin_default =
819 {
820   "default",
821   default_func,
822   BUILTIN_MENU,
823 #if 0
824   "default [NUM | `saved']",
825   "Set the default entry to entry number NUM (if not specified, it is"
826   " 0, the first entry) or the entry number saved by savedefault."
827 #endif
828 };
829 
830 
831 #ifdef GRUB_UTIL
832 /* device */
833 static int
834 device_func (char *arg, int flags)
835 {
836   char *drive = arg;
837   char *device;
838 
839   /* Get the drive number from DRIVE.  */
840   if (! set_device (drive))
841     return 1;
842 
843   /* Get the device argument.  */
844   device = skip_to (0, drive);
845 
846   /* Terminate DEVICE.  */
847   nul_terminate (device);
848 
849   if (! *device || ! check_device (device))
850     {
851       errnum = ERR_FILE_NOT_FOUND;
852       return 1;
853     }
854 
855   assign_device_name (current_drive, device);
856 
857   return 0;
858 }
859 
860 static struct builtin builtin_device =
861 {
862   "device",
863   device_func,
864   BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
865   "device DRIVE DEVICE",
866   "Specify DEVICE as the actual drive for a BIOS drive DRIVE. This command"
867   " can be used only in the grub shell."
868 };
869 #endif /* GRUB_UTIL */
870 
871 #ifdef SUPPORT_NETBOOT
872 /* Debug Function for RPC */
873 #ifdef RPC_DEBUG
874 /* portmap */
875 static int
876 portmap_func (char *arg, int flags)
877 {
878 	int port, prog, ver;
879 	if (! grub_eth_probe ()){
880 		grub_printf ("No ethernet card found.\n");
881 		errnum = ERR_DEV_VALUES;
882 		return 1;
883 	}
884 	if ((prog = getdec(&arg)) == -1){
885 		grub_printf("Error prog number\n");
886 		return 1;
887 	}
888 	arg = skip_to (0, arg);
889 	if ((ver = getdec(&arg)) == -1){
890 		grub_printf("Error ver number\n");
891 		return 1;
892 	}
893 	port = __pmapudp_getport(ARP_SERVER, prog, ver);
894 	printf("portmap getport %d", port);
895 	return 0;
896 }
897 
898 static struct builtin builtin_portmap =
899 {
900 	"portmap",
901 	portmap_func,
902 	BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
903 	"portmap prog_number vers_number",
904 	"Do portmap with the prog_number and vers_number"
905 };
906 #endif /* RPC_DEBUG */
907 
908 /* dhcp */
909 static int
910 dhcp_func (char *arg, int flags)
911 {
912   int with_configfile = 0;
913 
914   if (grub_memcmp (arg, "--with-configfile", sizeof ("--with-configfile") - 1)
915       == 0)
916     {
917       with_configfile = 1;
918       arg = skip_to (0, arg);
919     }
920 
921   if (! dhcp ())
922     {
923       if (errnum == ERR_NONE)
924 	errnum = ERR_DEV_VALUES;
925 
926       return 1;
927     }
928 
929   /* Notify the configuration.  */
930   print_network_configuration ();
931 
932   /* XXX: this can cause an endless loop, but there is no easy way to
933      detect such a loop unfortunately.  */
934   if (with_configfile)
935     configfile_func (config_file, flags);
936   else
937     solaris_config_file();
938 
939   return 0;
940 }
941 
942 static int
943 test_config_file(char *menufile)
944 {
945 	int err;
946 
947 	/*
948 	 * If the file exists, make it the default. Else, fallback
949 	 * to what it was.  Make sure we don't change errnum in the
950 	 * process.
951 	 */
952 	err = errnum;
953 	if (grub_open(menufile)) {
954 		grub_strcpy(config_file, menufile);
955 		grub_close();
956 		errnum = err;
957 		return (1);
958 	}
959 	errnum = err;
960 	return (0);
961 }
962 
963 static void solaris_config_file (void)
964 {
965 	static char menufile[64];
966 	static char hexdigit[] = "0123456789ABCDEF";
967 	char *c = menufile;
968 	int i;
969 
970 	/*
971 	 * if DHCP option 150 has been provided, config_file will
972 	 * already contain the string, try it.
973 	 */
974 	if (configfile_origin == CFG_150) {
975 		if (test_config_file(config_file))
976 			return;
977 	}
978 
979 	/*
980 	 * try to find host (MAC address) specific configfile:
981 	 * menu.lst.01<ether_addr>
982 	 */
983 	grub_strcpy(c, "menu.lst.01");
984 	c += grub_strlen(c);
985 	for (i = 0; i < ETH_ALEN; i++) {
986 		unsigned char b = arptable[ARP_CLIENT].node[i];
987 		*c++ = hexdigit[b >> 4];
988 		*c++ = hexdigit[b & 0xf];
989 	}
990 	*c = 0;
991 	configfile_origin = CFG_MAC;
992 	if (test_config_file(menufile))
993 		return;
994 
995 	/*
996 	 * try to find a configfile derived from the DHCP/bootp
997 	 * BootFile string: menu.lst.<BootFile>
998 	 */
999 	if (bootfile != NULL && bootfile[0] != 0) {
1000 		c = menufile;
1001 		grub_strcpy(c, "menu.lst.");
1002 		c += grub_strlen("menu.lst.");
1003 		i = grub_strlen("pxegrub.");
1004 		if (grub_memcmp(bootfile, "pxegrub.", i) == 0)
1005 			grub_strcpy(c, bootfile + i);
1006 		else
1007 			grub_strcpy(c, bootfile);
1008 		configfile_origin = CFG_BOOTFILE;
1009 		if (test_config_file(menufile))
1010 			return;
1011 	}
1012 
1013 	/*
1014 	 * Default to hard coded "/boot/grub/menu.lst" config file.
1015 	 * This is the last resort, so there's no need to test it,
1016 	 * as there's nothing else to try.
1017 	 */
1018 	char *cp = config_file;
1019 	/* skip leading slashes for tftp */
1020 	while (*cp == '/')
1021 		++cp;
1022   	grub_memmove (config_file, cp, strlen(cp) + 1);
1023 	configfile_origin = CFG_HARDCODED;
1024 }
1025 
1026 static struct builtin builtin_dhcp =
1027 {
1028   "dhcp",
1029   dhcp_func,
1030   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
1031   "dhcp",
1032   "Initialize a network device via DHCP."
1033 };
1034 #endif /* SUPPORT_NETBOOT */
1035 
1036 static int terminal_func (char *arg, int flags);
1037 
1038 static int verbose_func(char *arg, int flags) {
1039 
1040     if (grub_strcmp(arg, "off") == 0) {
1041       silent.status = DEFER_SILENT;
1042       return;
1043     } else
1044         if (flags == BUILTIN_CMDLINE) {
1045           silent.status = DEFER_VERBOSE;
1046           return;
1047         }
1048 
1049   silent.status = VERBOSE;
1050 
1051   /* get back to text console */
1052   if (current_term->shutdown) {
1053     (*current_term->shutdown)();
1054     current_term = term_table; /* assumption: console is first */
1055   }
1056 
1057   /* dump the buffer */
1058   if (!silent.looped) {
1059     /* if the buffer hasn't looped, just print it */
1060     printf("%s", silent.buffer);
1061   } else {
1062     /*
1063      * If the buffer has looped, first print the oldest part of the buffer,
1064      * which is one past the current null. Then print the newer part which
1065      * starts at the beginning of the buffer.
1066      */
1067     printf("%s", silent.buffer_start + 1);
1068     printf("%s", silent.buffer);
1069   }
1070 
1071   return 0;
1072 }
1073 
1074 static struct builtin builtin_verbose =
1075 {
1076   "verbose",
1077   verbose_func,
1078   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_SCRIPT | BUILTIN_HELP_LIST,
1079   "verbose",
1080   "Verbose output during menu entry (script) execution."
1081 };
1082 
1083 #ifdef SUPPORT_GRAPHICS
1084 
1085 static int splashimage_func(char *arg, int flags) {
1086     char splashimage[64];
1087     int i;
1088 
1089     /* filename can only be 64 characters due to our buffer size */
1090     if (strlen(arg) > 63)
1091 	return 1;
1092 
1093     if (flags == BUILTIN_SCRIPT)
1094         flags = BUILTIN_CMDLINE;
1095 
1096     if (flags == BUILTIN_CMDLINE) {
1097 	if (!grub_open(arg))
1098 	    return 1;
1099 	grub_close();
1100     }
1101 
1102     strcpy(splashimage, arg);
1103 
1104     /* get rid of TERM_NEED_INIT from the graphics terminal. */
1105     for (i = 0; term_table[i].name; i++) {
1106 	if (grub_strcmp (term_table[i].name, "graphics") == 0) {
1107 	    term_table[i].flags &= ~TERM_NEED_INIT;
1108 	    break;
1109 	}
1110     }
1111 
1112     graphics_set_splash(splashimage);
1113 
1114     if (flags == BUILTIN_CMDLINE && graphics_inited) {
1115 	/*
1116 	 * calling graphics_end() here flickers the screen black. OTOH not
1117 	 * calling it gets us odd plane interlacing / early palette switching ?
1118 	 * ideally one should figure out how to double buffer and switch...
1119 	 */
1120 	graphics_end();
1121 	graphics_init();
1122 	graphics_cls();
1123     }
1124 
1125     /*
1126      * This call does not explicitly initialize graphics mode, but rather
1127      * simply sets the terminal type unless we're in command line mode and
1128      * call this function while in terminal mode.
1129      */
1130     terminal_func("graphics", flags);
1131 
1132     reset_term = 0;
1133 
1134     return 0;
1135 }
1136 
1137 static struct builtin builtin_splashimage =
1138 {
1139   "splashimage",
1140   splashimage_func,
1141   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_SCRIPT | BUILTIN_HELP_LIST,
1142   "splashimage FILE",
1143   "Load FILE as the background image when in graphics mode."
1144 };
1145 
1146 
1147 /* foreground */
1148 static int
1149 foreground_func(char *arg, int flags)
1150 {
1151     if (grub_strlen(arg) == 6) {
1152 	int r = ((hex(arg[0]) << 4) | hex(arg[1])) >> 2;
1153 	int g = ((hex(arg[2]) << 4) | hex(arg[3])) >> 2;
1154 	int b = ((hex(arg[4]) << 4) | hex(arg[5])) >> 2;
1155 
1156 	foreground = (r << 16) | (g << 8) | b;
1157 	if (graphics_inited)
1158 	    graphics_set_palette(15, r, g, b);
1159 
1160 	return (0);
1161     }
1162 
1163     return (1);
1164 }
1165 
1166 static struct builtin builtin_foreground =
1167 {
1168   "foreground",
1169   foreground_func,
1170   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST | BUILTIN_SCRIPT,
1171   "foreground RRGGBB",
1172   "Sets the foreground color when in graphics mode."
1173   "RR is red, GG is green, and BB blue. Numbers must be in hexadecimal."
1174 };
1175 
1176 
1177 /* background */
1178 static int
1179 background_func(char *arg, int flags)
1180 {
1181     if (grub_strlen(arg) == 6) {
1182 	int r = ((hex(arg[0]) << 4) | hex(arg[1])) >> 2;
1183 	int g = ((hex(arg[2]) << 4) | hex(arg[3])) >> 2;
1184 	int b = ((hex(arg[4]) << 4) | hex(arg[5])) >> 2;
1185 
1186 	background = (r << 16) | (g << 8) | b;
1187 	if (graphics_inited)
1188 	    graphics_set_palette(0, r, g, b);
1189 	return (0);
1190     }
1191 
1192     return (1);
1193 }
1194 
1195 static struct builtin builtin_background =
1196 {
1197   "background",
1198   background_func,
1199   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST | BUILTIN_SCRIPT,
1200   "background RRGGBB",
1201   "Sets the background color when in graphics mode."
1202   "RR is red, GG is green, and BB blue. Numbers must be in hexadecimal."
1203 };
1204 
1205 #endif /* SUPPORT_GRAPHICS */
1206 
1207 
1208 /* clear */
1209 static int
1210 clear_func()
1211 {
1212   if (current_term->cls)
1213     current_term->cls();
1214 
1215   return 0;
1216 }
1217 
1218 static struct builtin builtin_clear =
1219 {
1220   "clear",
1221   clear_func,
1222   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
1223   "clear",
1224   "Clear the screen"
1225 };
1226 
1227 /* displayapm */
1228 static int
1229 displayapm_func (char *arg, int flags)
1230 {
1231   if (mbi.flags & MB_INFO_APM_TABLE)
1232     {
1233       grub_printf ("APM BIOS information:\n"
1234 		   " Version:          0x%x\n"
1235 		   " 32-bit CS:        0x%x\n"
1236 		   " Offset:           0x%x\n"
1237 		   " 16-bit CS:        0x%x\n"
1238 		   " 16-bit DS:        0x%x\n"
1239 		   " 32-bit CS length: 0x%x\n"
1240 		   " 16-bit CS length: 0x%x\n"
1241 		   " 16-bit DS length: 0x%x\n",
1242 		   (unsigned) apm_bios_info.version,
1243 		   (unsigned) apm_bios_info.cseg,
1244 		   apm_bios_info.offset,
1245 		   (unsigned) apm_bios_info.cseg_16,
1246 		   (unsigned) apm_bios_info.dseg_16,
1247 		   (unsigned) apm_bios_info.cseg_len,
1248 		   (unsigned) apm_bios_info.cseg_16_len,
1249 		   (unsigned) apm_bios_info.dseg_16_len);
1250     }
1251   else
1252     {
1253       grub_printf ("No APM BIOS found or probe failed\n");
1254     }
1255 
1256   return 0;
1257 }
1258 
1259 static struct builtin builtin_displayapm =
1260 {
1261   "displayapm",
1262   displayapm_func,
1263   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
1264   "displayapm",
1265   "Display APM BIOS information."
1266 };
1267 
1268 
1269 /* displaymem */
1270 static int
1271 displaymem_func (char *arg, int flags)
1272 {
1273   if (get_eisamemsize () != -1)
1274     grub_printf (" EISA Memory BIOS Interface is present\n");
1275   if (get_mmap_entry ((void *) SCRATCHADDR, 0) != 0
1276       || *((int *) SCRATCHADDR) != 0)
1277     grub_printf (" Address Map BIOS Interface is present\n");
1278 
1279   grub_printf (" Lower memory: %uK, "
1280 	       "Upper memory (to first chipset hole): %uK\n",
1281 	       mbi.mem_lower, mbi.mem_upper);
1282 
1283   if (min_mem64 != 0)
1284   	grub_printf (" Memory limit for 64-bit ISADIR expansion: %uMB\n",
1285 	    min_mem64);
1286 
1287   if (mbi.flags & MB_INFO_MEM_MAP)
1288     {
1289       struct AddrRangeDesc *map = (struct AddrRangeDesc *) mbi.mmap_addr;
1290       int end_addr = mbi.mmap_addr + mbi.mmap_length;
1291 
1292       grub_printf (" [Address Range Descriptor entries "
1293 		   "immediately follow (values are 64-bit)]\n");
1294       while (end_addr > (int) map)
1295 	{
1296 	  char *str;
1297 
1298 	  if (map->Type == MB_ARD_MEMORY)
1299 	    str = "Usable RAM";
1300 	  else
1301 	    str = "Reserved";
1302 	  grub_printf ("   %s:  Base Address:  0x%x X 4GB + 0x%x,\n"
1303 		"      Length:   0x%x X 4GB + 0x%x bytes\n",
1304 		str,
1305 		(unsigned long) (map->BaseAddr >> 32),
1306 		(unsigned long) (map->BaseAddr & 0xFFFFFFFF),
1307 		(unsigned long) (map->Length >> 32),
1308 		(unsigned long) (map->Length & 0xFFFFFFFF));
1309 
1310 	  map = ((struct AddrRangeDesc *) (((int) map) + 4 + map->size));
1311 	}
1312     }
1313 
1314   return 0;
1315 }
1316 
1317 static struct builtin builtin_displaymem =
1318 {
1319   "displaymem",
1320   displaymem_func,
1321   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
1322   "displaymem",
1323   "Display what GRUB thinks the system address space map of the"
1324   " machine is, including all regions of physical RAM installed."
1325 };
1326 
1327 
1328 /* dump FROM TO */
1329 #ifdef GRUB_UTIL
1330 static int
1331 dump_func (char *arg, int flags)
1332 {
1333   char *from, *to;
1334   FILE *fp;
1335   char c;
1336 
1337   from = arg;
1338   to = skip_to (0, arg);
1339   if (! *from || ! *to)
1340     {
1341       errnum = ERR_BAD_ARGUMENT;
1342       return 1;
1343     }
1344 
1345   nul_terminate (from);
1346   nul_terminate (to);
1347 
1348   if (! grub_open (from))
1349     return 1;
1350 
1351   fp = fopen (to, "w");
1352   if (! fp)
1353     {
1354       errnum = ERR_WRITE;
1355       return 1;
1356     }
1357 
1358   while (grub_read (&c, 1))
1359     if (fputc (c, fp) == EOF)
1360       {
1361 	errnum = ERR_WRITE;
1362 	fclose (fp);
1363 	return 1;
1364       }
1365 
1366   if (fclose (fp) == EOF)
1367     {
1368       errnum = ERR_WRITE;
1369       return 1;
1370     }
1371 
1372   grub_close ();
1373   return 0;
1374 }
1375 
1376 static struct builtin builtin_dump =
1377   {
1378     "dump",
1379     dump_func,
1380     BUILTIN_CMDLINE,
1381     "dump FROM TO",
1382     "Dump the contents of the file FROM to the file TO. FROM must be"
1383     " a GRUB file and TO must be an OS file."
1384   };
1385 #endif /* GRUB_UTIL */
1386 
1387 
1388 static char embed_info[32];
1389 /* embed */
1390 /* Embed a Stage 1.5 in the first cylinder after MBR or in the
1391    bootloader block in a FFS.  */
1392 static int
1393 embed_func (char *arg, int flags)
1394 {
1395   char *stage1_5;
1396   char *device;
1397   char *stage1_5_buffer = (char *) RAW_ADDR (0x100000);
1398   int len, size;
1399   int sector;
1400 
1401   stage1_5 = arg;
1402   device = skip_to (0, stage1_5);
1403 
1404   /* Open a Stage 1.5.  */
1405   if (! grub_open (stage1_5))
1406     return 1;
1407 
1408   /* Read the whole of the Stage 1.5.  */
1409   len = grub_read (stage1_5_buffer, -1);
1410   grub_close ();
1411 
1412   if (errnum)
1413     return 1;
1414 
1415   size = (len + SECTOR_SIZE - 1) / SECTOR_SIZE;
1416 
1417   /* Get the device where the Stage 1.5 will be embedded.  */
1418   set_device (device);
1419   if (errnum)
1420     return 1;
1421 
1422   if (current_partition == 0xFFFFFF)
1423     {
1424       /* Embed it after the MBR.  */
1425 
1426       char mbr[SECTOR_SIZE];
1427       char ezbios_check[2*SECTOR_SIZE];
1428       int i;
1429 
1430       /* Open the partition.  */
1431       if (! open_partition ())
1432 	return 1;
1433 
1434       /* No floppy has MBR.  */
1435       if (! (current_drive & 0x80))
1436 	{
1437 	  errnum = ERR_DEV_VALUES;
1438 	  return 1;
1439 	}
1440 
1441       /* Read the MBR of CURRENT_DRIVE.  */
1442       if (! rawread (current_drive, PC_MBR_SECTOR, 0, SECTOR_SIZE, mbr))
1443 	return 1;
1444 
1445       /* Sanity check.  */
1446       if (! PC_MBR_CHECK_SIG (mbr))
1447 	{
1448 	  errnum = ERR_BAD_PART_TABLE;
1449 	  return 1;
1450 	}
1451 
1452       /* Check if the disk can store the Stage 1.5.  */
1453       for (i = 0; i < 4; i++)
1454 	if (PC_SLICE_TYPE (mbr, i) && PC_SLICE_START (mbr, i) - 1 < size)
1455 	  {
1456 	    errnum = ERR_NO_DISK_SPACE;
1457 	    return 1;
1458 	  }
1459 
1460       /* Check for EZ-BIOS signature. It should be in the third
1461        * sector, but due to remapping it can appear in the second, so
1462        * load and check both.
1463        */
1464       if (! rawread (current_drive, 1, 0, 2 * SECTOR_SIZE, ezbios_check))
1465 	return 1;
1466 
1467       if (! memcmp (ezbios_check + 3, "AERMH", 5)
1468 	  || ! memcmp (ezbios_check + 512 + 3, "AERMH", 5))
1469 	{
1470 	  /* The space after the MBR is used by EZ-BIOS which we must
1471 	   * not overwrite.
1472 	   */
1473 	  errnum = ERR_NO_DISK_SPACE;
1474 	  return 1;
1475 	}
1476 
1477       sector = 1;
1478     }
1479   else
1480     {
1481       /* Embed it in the bootloader block in the filesystem.  */
1482       int start_sector;
1483 
1484       /* Open the partition.  */
1485       if (! open_device ())
1486 	return 1;
1487 
1488       /* Check if the current slice supports embedding.  */
1489       if (fsys_table[fsys_type].embed_func == 0
1490 	  || ! fsys_table[fsys_type].embed_func (&start_sector, size))
1491 	{
1492 	  errnum = ERR_DEV_VALUES;
1493 	  return 1;
1494 	}
1495 
1496       sector = part_start + start_sector;
1497     }
1498 
1499   /* Clear the cache.  */
1500   buf_track = BUF_CACHE_INVALID;
1501 
1502   /* Now perform the embedding.  */
1503   if (! devwrite (sector - part_start, size, stage1_5_buffer))
1504     return 1;
1505 
1506   grub_printf (" %d sectors are embedded.\n", size);
1507   grub_sprintf (embed_info, "%d+%d", sector - part_start, size);
1508   return 0;
1509 }
1510 
1511 static struct builtin builtin_embed =
1512 {
1513   "embed",
1514   embed_func,
1515   BUILTIN_CMDLINE,
1516   "embed STAGE1_5 DEVICE",
1517   "Embed the Stage 1.5 STAGE1_5 in the sectors after MBR if DEVICE"
1518   " is a drive, or in the \"bootloader\" area if DEVICE is a FFS partition."
1519   " Print the number of sectors which STAGE1_5 occupies if successful."
1520 };
1521 
1522 
1523 /* fallback */
1524 static int
1525 fallback_func (char *arg, int flags)
1526 {
1527   int i = 0;
1528 
1529   while (*arg)
1530     {
1531       int entry;
1532       int j;
1533 
1534       if (! safe_parse_maxint (&arg, &entry))
1535 	return 1;
1536 
1537       /* Remove duplications to prevent infinite looping.  */
1538       for (j = 0; j < i; j++)
1539 	if (entry == fallback_entries[j])
1540 	  break;
1541       if (j != i)
1542 	continue;
1543 
1544       fallback_entries[i++] = entry;
1545       if (i == MAX_FALLBACK_ENTRIES)
1546 	break;
1547 
1548       arg = skip_to (0, arg);
1549     }
1550 
1551   if (i < MAX_FALLBACK_ENTRIES)
1552     fallback_entries[i] = -1;
1553 
1554   fallback_entryno = (i == 0) ? -1 : 0;
1555 
1556   return 0;
1557 }
1558 
1559 static struct builtin builtin_fallback =
1560 {
1561   "fallback",
1562   fallback_func,
1563   BUILTIN_MENU,
1564 #if 0
1565   "fallback NUM...",
1566   "Go into unattended boot mode: if the default boot entry has any"
1567   " errors, instead of waiting for the user to do anything, it"
1568   " immediately starts over using the NUM entry (same numbering as the"
1569   " `default' command). This obviously won't help if the machine"
1570   " was rebooted by a kernel that GRUB loaded."
1571 #endif
1572 };
1573 
1574 
1575 
1576 void
1577 set_root (char *root, unsigned long drive, unsigned long part)
1578 {
1579   int bsd_part = (part >> 8) & 0xFF;
1580   int pc_slice = part >> 16;
1581 
1582   if (bsd_part == 0xFF) {
1583     grub_sprintf (root, "(hd%d,%d)\n", drive - 0x80, pc_slice);
1584   } else {
1585     grub_sprintf (root, "(hd%d,%d,%c)\n",
1586 		 drive - 0x80, pc_slice, bsd_part + 'a');
1587   }
1588 }
1589 
1590 static int
1591 find_common (char *arg, char *root, int for_root, int flags)
1592 {
1593   char *filename = NULL;
1594   static char argpart[32];
1595   static char device[32];
1596   char *tmp_argpart = NULL;
1597   unsigned long drive;
1598   unsigned long tmp_drive = saved_drive;
1599   unsigned long tmp_partition = saved_partition;
1600   int got_file = 0;
1601   static char bootsign[BOOTSIGN_LEN];
1602 
1603   /*
1604    * If argument has partition information (findroot command only), then
1605    * it can't be a floppy
1606    */
1607   if (for_root && arg[0] == '(') {
1608 	tmp_argpart = grub_strchr(arg + 1, ',');
1609         if (tmp_argpart == NULL)
1610 		goto out;
1611 	grub_strcpy(argpart, tmp_argpart);
1612 	*tmp_argpart = '\0';
1613 	arg++;
1614         grub_sprintf(bootsign, "%s/%s", BOOTSIGN_DIR, arg);
1615 	filename = bootsign;
1616 	goto harddisk;
1617   } else if (for_root && !grub_strchr(arg, '/')) {
1618 	/* Boot signature without partition/slice information */
1619         grub_sprintf(bootsign, "%s/%s", BOOTSIGN_DIR, arg);
1620 	filename = bootsign;
1621   } else {
1622 	/* plain vanilla find cmd */
1623 	filename = arg;
1624   }
1625 
1626   /* Floppies.  */
1627   for (drive = 0; drive < 8; drive++)
1628     {
1629       current_drive = drive;
1630       current_partition = 0xFFFFFF;
1631 
1632       if (open_device ())
1633 	{
1634 	  saved_drive = current_drive;
1635 	  saved_partition = current_partition;
1636 	  if (grub_open (filename))
1637 	    {
1638 	      grub_close ();
1639 	      got_file = 1;
1640 	      if (for_root) {
1641 		 grub_sprintf(root, "(fd%d)", drive);
1642 		 goto out;
1643 	      } else
1644 	         grub_printf (" (fd%d)\n", drive);
1645 	    }
1646 	}
1647 
1648       errnum = ERR_NONE;
1649     }
1650 
1651 harddisk:
1652   /* Hard disks.  */
1653   for (drive = 0x80; drive < 0x88; drive++)
1654     {
1655       unsigned long part = 0xFFFFFF;
1656       unsigned long start, len, offset, ext_offset;
1657       int type, entry;
1658       char buf[SECTOR_SIZE];
1659 
1660       if (for_root && tmp_argpart) {
1661 	grub_sprintf(device, "(hd%d%s", drive - 0x80, argpart);
1662 	set_device(device);
1663         errnum = ERR_NONE;
1664 	part = current_partition;
1665 	if (open_device ()) {
1666 	   saved_drive = current_drive;
1667 	   saved_partition = current_partition;
1668            errnum = ERR_NONE;
1669 	   if (grub_open (filename)) {
1670 	      grub_close ();
1671 	      got_file = 1;
1672 	      if (is_zfs_mount == 0) {
1673 	        set_root(root, current_drive, current_partition);
1674 	        goto out;
1675 	      } else {
1676 		best_drive = current_drive;
1677 		best_part = current_partition;
1678 	      }
1679            }
1680 	}
1681         errnum = ERR_NONE;
1682 	continue;
1683       }
1684       current_drive = drive;
1685       while (next_partition (drive, 0xFFFFFF, &part, &type,
1686 			     &start, &len, &offset, &entry,
1687 			     &ext_offset, buf))
1688 	{
1689 	  if (type != PC_SLICE_TYPE_NONE
1690 	      && ! IS_PC_SLICE_TYPE_BSD (type)
1691 	      && ! IS_PC_SLICE_TYPE_EXTENDED (type))
1692 	    {
1693 	      current_partition = part;
1694 	      if (open_device ())
1695 		{
1696 		  saved_drive = current_drive;
1697 		  saved_partition = current_partition;
1698 		  if (grub_open (filename))
1699 		    {
1700 		      char tmproot[32];
1701 
1702 		      grub_close ();
1703 		      got_file = 1;
1704 		      set_root(tmproot, drive, part);
1705 		      if (for_root) {
1706 		 	grub_memcpy(root, tmproot, sizeof(tmproot));
1707 			if (is_zfs_mount == 0) {
1708 			      goto out;
1709 			} else {
1710 			      best_drive = current_drive;
1711 			      best_part = current_partition;
1712 			}
1713 		      } else {
1714 			grub_printf("%s", tmproot);
1715 		      }
1716 		    }
1717 		}
1718 	    }
1719 
1720 	  /* We want to ignore any error here.  */
1721 	  errnum = ERR_NONE;
1722 	}
1723 
1724       /* next_partition always sets ERRNUM in the last call, so clear
1725 	 it.  */
1726       errnum = ERR_NONE;
1727     }
1728 
1729 out:
1730   if (is_zfs_mount && for_root) {
1731         set_root(root, best_drive, best_part);
1732 	buf_drive = -1;
1733   } else {
1734 	saved_drive = tmp_drive;
1735 	saved_partition = tmp_partition;
1736   }
1737   if (tmp_argpart)
1738 	*tmp_argpart = ',';
1739 
1740   if (got_file)
1741     {
1742       errnum = ERR_NONE;
1743       return 0;
1744     }
1745 
1746   errnum = ERR_FILE_NOT_FOUND;
1747   return 1;
1748 }
1749 
1750 /* find */
1751 /* Search for the filename ARG in all of partitions.  */
1752 static int
1753 find_func (char *arg, int flags)
1754 {
1755 	return (find_common(arg, NULL, 0, flags));
1756 }
1757 
1758 static struct builtin builtin_find =
1759 {
1760   "find",
1761   find_func,
1762   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
1763   "find FILENAME",
1764   "Search for the filename FILENAME in all of partitions and print the list of"
1765   " the devices which contain the file."
1766 };
1767 
1768 
1769 /* fstest */
1770 static int
1771 fstest_func (char *arg, int flags)
1772 {
1773   if (disk_read_hook)
1774     {
1775       disk_read_hook = NULL;
1776       printf (" Filesystem tracing is now off\n");
1777     }
1778   else
1779     {
1780       disk_read_hook = disk_read_print_func;
1781       printf (" Filesystem tracing is now on\n");
1782     }
1783 
1784   return 0;
1785 }
1786 
1787 static struct builtin builtin_fstest =
1788 {
1789   "fstest",
1790   fstest_func,
1791   BUILTIN_CMDLINE,
1792   "fstest",
1793   "Toggle filesystem test mode."
1794 };
1795 
1796 
1797 /* geometry */
1798 static int
1799 geometry_func (char *arg, int flags)
1800 {
1801   struct geometry geom;
1802   char *msg;
1803   char *device = arg;
1804 #ifdef GRUB_UTIL
1805   char *ptr;
1806 #endif
1807 
1808   /* Get the device number.  */
1809   set_device (device);
1810   if (errnum)
1811     return 1;
1812 
1813   /* Check for the geometry.  */
1814   if (get_diskinfo (current_drive, &geom))
1815     {
1816       errnum = ERR_NO_DISK;
1817       return 1;
1818     }
1819 
1820   /* Attempt to read the first sector, because some BIOSes turns out not
1821      to support LBA even though they set the bit 0 in the support
1822      bitmap, only after reading something actually.  */
1823   if (biosdisk (BIOSDISK_READ, current_drive, &geom, 0, 1, SCRATCHSEG))
1824     {
1825       errnum = ERR_READ;
1826       return 1;
1827     }
1828 
1829 #ifdef GRUB_UTIL
1830   ptr = skip_to (0, device);
1831   if (*ptr)
1832     {
1833       char *cylinder, *head, *sector, *total_sector;
1834       int num_cylinder, num_head, num_sector, num_total_sector;
1835 
1836       cylinder = ptr;
1837       head = skip_to (0, cylinder);
1838       sector = skip_to (0, head);
1839       total_sector = skip_to (0, sector);
1840       if (! safe_parse_maxint (&cylinder, &num_cylinder)
1841 	  || ! safe_parse_maxint (&head, &num_head)
1842 	  || ! safe_parse_maxint (&sector, &num_sector))
1843 	return 1;
1844 
1845       disks[current_drive].cylinders = num_cylinder;
1846       disks[current_drive].heads = num_head;
1847       disks[current_drive].sectors = num_sector;
1848 
1849       if (safe_parse_maxint (&total_sector, &num_total_sector))
1850 	disks[current_drive].total_sectors = num_total_sector;
1851       else
1852 	disks[current_drive].total_sectors
1853 	  = num_cylinder * num_head * num_sector;
1854       errnum = 0;
1855 
1856       geom = disks[current_drive];
1857       buf_drive = -1;
1858     }
1859 #endif /* GRUB_UTIL */
1860 
1861 #ifdef GRUB_UTIL
1862   msg = device_map[current_drive];
1863 #else
1864   if (geom.flags & BIOSDISK_FLAG_LBA_EXTENSION)
1865     msg = "LBA";
1866   else
1867     msg = "CHS";
1868 #endif
1869 
1870   grub_printf ("drive 0x%x: C/H/S = %d/%d/%d, "
1871 	       "The number of sectors = %u, %s\n",
1872 	       current_drive,
1873 	       geom.cylinders, geom.heads, geom.sectors,
1874 	       geom.total_sectors, msg);
1875   real_open_partition (1);
1876 
1877   return 0;
1878 }
1879 
1880 static struct builtin builtin_geometry =
1881 {
1882   "geometry",
1883   geometry_func,
1884   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
1885   "geometry DRIVE [CYLINDER HEAD SECTOR [TOTAL_SECTOR]]",
1886   "Print the information for a drive DRIVE. In the grub shell, you can"
1887   " set the geometry of the drive arbitrarily. The number of the cylinders,"
1888   " the one of the heads, the one of the sectors and the one of the total"
1889   " sectors are set to CYLINDER, HEAD, SECTOR and TOTAL_SECTOR,"
1890   " respectively. If you omit TOTAL_SECTOR, then it will be calculated based"
1891   " on the C/H/S values automatically."
1892 };
1893 
1894 
1895 /* halt */
1896 static int
1897 halt_func (char *arg, int flags)
1898 {
1899   int no_apm;
1900 
1901   no_apm = (grub_memcmp (arg, "--no-apm", 8) == 0);
1902   grub_halt (no_apm);
1903 
1904   /* Never reach here.  */
1905   return 1;
1906 }
1907 
1908 static struct builtin builtin_halt =
1909 {
1910   "halt",
1911   halt_func,
1912   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
1913   "halt [--no-apm]",
1914   "Halt your system. If APM is avaiable on it, turn off the power using"
1915   " the APM BIOS, unless you specify the option `--no-apm'."
1916 };
1917 
1918 
1919 /* help */
1920 #define MAX_SHORT_DOC_LEN	39
1921 #define MAX_LONG_DOC_LEN	66
1922 
1923 static int
1924 help_func (char *arg, int flags)
1925 {
1926   int all = 0;
1927 
1928   if (grub_memcmp (arg, "--all", sizeof ("--all") - 1) == 0)
1929     {
1930       all = 1;
1931       arg = skip_to (0, arg);
1932     }
1933 
1934   if (! *arg)
1935     {
1936       /* Invoked with no argument. Print the list of the short docs.  */
1937       struct builtin **builtin;
1938       int left = 1;
1939 
1940       for (builtin = builtin_table; *builtin != 0; builtin++)
1941 	{
1942 	  int len;
1943 	  int i;
1944 
1945 	  /* If this cannot be used in the command-line interface,
1946 	     skip this.  */
1947 	  if (! ((*builtin)->flags & BUILTIN_CMDLINE))
1948 	    continue;
1949 
1950 	  /* If this doesn't need to be listed automatically and "--all"
1951 	     is not specified, skip this.  */
1952 	  if (! all && ! ((*builtin)->flags & BUILTIN_HELP_LIST))
1953 	    continue;
1954 
1955 	  len = grub_strlen ((*builtin)->short_doc);
1956 	  /* If the length of SHORT_DOC is too long, truncate it.  */
1957 	  if (len > MAX_SHORT_DOC_LEN - 1)
1958 	    len = MAX_SHORT_DOC_LEN - 1;
1959 
1960 	  for (i = 0; i < len; i++)
1961 	    grub_putchar ((*builtin)->short_doc[i]);
1962 
1963 	  for (; i < MAX_SHORT_DOC_LEN; i++)
1964 	    grub_putchar (' ');
1965 
1966 	  if (! left)
1967 	    grub_putchar ('\n');
1968 
1969 	  left = ! left;
1970 	}
1971 
1972       /* If the last entry was at the left column, no newline was printed
1973 	 at the end.  */
1974       if (! left)
1975 	grub_putchar ('\n');
1976     }
1977   else
1978     {
1979       /* Invoked with one or more patterns.  */
1980       do
1981 	{
1982 	  struct builtin **builtin;
1983 	  char *next_arg;
1984 
1985 	  /* Get the next argument.  */
1986 	  next_arg = skip_to (0, arg);
1987 
1988 	  /* Terminate ARG.  */
1989 	  nul_terminate (arg);
1990 
1991 	  for (builtin = builtin_table; *builtin; builtin++)
1992 	    {
1993 	      /* Skip this if this is only for the configuration file.  */
1994 	      if (! ((*builtin)->flags & BUILTIN_CMDLINE))
1995 		continue;
1996 
1997 	      if (substring (arg, (*builtin)->name) < 1)
1998 		{
1999 		  char *doc = (*builtin)->long_doc;
2000 
2001 		  /* At first, print the name and the short doc.  */
2002 		  grub_printf ("%s: %s\n",
2003 			       (*builtin)->name, (*builtin)->short_doc);
2004 
2005 		  /* Print the long doc.  */
2006 		  while (*doc)
2007 		    {
2008 		      int len = grub_strlen (doc);
2009 		      int i;
2010 
2011 		      /* If LEN is too long, fold DOC.  */
2012 		      if (len > MAX_LONG_DOC_LEN)
2013 			{
2014 			  /* Fold this line at the position of a space.  */
2015 			  for (len = MAX_LONG_DOC_LEN; len > 0; len--)
2016 			    if (doc[len - 1] == ' ')
2017 			      break;
2018 			}
2019 
2020 		      grub_printf ("    ");
2021 		      for (i = 0; i < len; i++)
2022 			grub_putchar (*doc++);
2023 		      grub_putchar ('\n');
2024 		    }
2025 		}
2026 	    }
2027 
2028 	  arg = next_arg;
2029 	}
2030       while (*arg);
2031     }
2032 
2033   return 0;
2034 }
2035 
2036 static struct builtin builtin_help =
2037 {
2038   "help",
2039   help_func,
2040   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
2041   "help [--all] [PATTERN ...]",
2042   "Display helpful information about builtin commands. Not all commands"
2043   " aren't shown without the option `--all'."
2044 };
2045 
2046 
2047 /* hiddenmenu */
2048 static int
2049 hiddenmenu_func (char *arg, int flags)
2050 {
2051   show_menu = 0;
2052   return 0;
2053 }
2054 
2055 static struct builtin builtin_hiddenmenu =
2056 {
2057   "hiddenmenu",
2058   hiddenmenu_func,
2059   BUILTIN_MENU,
2060 #if 0
2061   "hiddenmenu",
2062   "Hide the menu."
2063 #endif
2064 };
2065 
2066 
2067 /* hide */
2068 static int
2069 hide_func (char *arg, int flags)
2070 {
2071   if (! set_device (arg))
2072     return 1;
2073 
2074   if (! set_partition_hidden_flag (1))
2075     return 1;
2076 
2077   return 0;
2078 }
2079 
2080 static struct builtin builtin_hide =
2081 {
2082   "hide",
2083   hide_func,
2084   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
2085   "hide PARTITION",
2086   "Hide PARTITION by setting the \"hidden\" bit in"
2087   " its partition type code."
2088 };
2089 
2090 
2091 #ifdef SUPPORT_NETBOOT
2092 /* ifconfig */
2093 static int
2094 ifconfig_func (char *arg, int flags)
2095 {
2096   char *svr = 0, *ip = 0, *gw = 0, *sm = 0;
2097 
2098   if (! grub_eth_probe ())
2099     {
2100       grub_printf ("No ethernet card found.\n");
2101       errnum = ERR_DEV_VALUES;
2102       return 1;
2103     }
2104 
2105   while (*arg)
2106     {
2107       if (! grub_memcmp ("--server=", arg, sizeof ("--server=") - 1))
2108 	svr = arg + sizeof("--server=") - 1;
2109       else if (! grub_memcmp ("--address=", arg, sizeof ("--address=") - 1))
2110 	ip = arg + sizeof ("--address=") - 1;
2111       else if (! grub_memcmp ("--gateway=", arg, sizeof ("--gateway=") - 1))
2112 	gw = arg + sizeof ("--gateway=") - 1;
2113       else if (! grub_memcmp ("--mask=", arg, sizeof("--mask=") - 1))
2114 	sm = arg + sizeof ("--mask=") - 1;
2115       else
2116 	{
2117 	  errnum = ERR_BAD_ARGUMENT;
2118 	  return 1;
2119 	}
2120 
2121       arg = skip_to (0, arg);
2122     }
2123 
2124   if (! ifconfig (ip, sm, gw, svr))
2125     {
2126       errnum = ERR_BAD_ARGUMENT;
2127       return 1;
2128     }
2129 
2130   print_network_configuration ();
2131   return 0;
2132 }
2133 
2134 static struct builtin builtin_ifconfig =
2135 {
2136   "ifconfig",
2137   ifconfig_func,
2138   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
2139   "ifconfig [--address=IP] [--gateway=IP] [--mask=MASK] [--server=IP]",
2140   "Configure the IP address, the netmask, the gateway and the server"
2141   " address or print current network configuration."
2142 };
2143 #endif /* SUPPORT_NETBOOT */
2144 
2145 
2146 /* impsprobe */
2147 static int
2148 impsprobe_func (char *arg, int flags)
2149 {
2150 #ifdef GRUB_UTIL
2151   /* In the grub shell, we cannot probe IMPS.  */
2152   errnum = ERR_UNRECOGNIZED;
2153   return 1;
2154 #else /* ! GRUB_UTIL */
2155   if (!imps_probe ())
2156     printf (" No MPS information found or probe failed\n");
2157 
2158   return 0;
2159 #endif /* ! GRUB_UTIL */
2160 }
2161 
2162 static struct builtin builtin_impsprobe =
2163 {
2164   "impsprobe",
2165   impsprobe_func,
2166   BUILTIN_CMDLINE,
2167   "impsprobe",
2168   "Probe the Intel Multiprocessor Specification 1.1 or 1.4"
2169   " configuration table and boot the various CPUs which are found into"
2170   " a tight loop."
2171 };
2172 
2173 /* initrd */
2174 static int
2175 initrd_func (char *arg, int flags)
2176 {
2177   switch (kernel_type)
2178     {
2179     case KERNEL_TYPE_LINUX:
2180     case KERNEL_TYPE_BIG_LINUX:
2181       if (! load_initrd (arg))
2182 	return 1;
2183       break;
2184 
2185     default:
2186       errnum = ERR_NEED_LX_KERNEL;
2187       return 1;
2188     }
2189 
2190   return 0;
2191 }
2192 
2193 static struct builtin builtin_initrd =
2194 {
2195   "initrd",
2196   initrd_func,
2197   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
2198   "initrd FILE [ARG ...]",
2199   "Load an initial ramdisk FILE for a Linux format boot image and set the"
2200   " appropriate parameters in the Linux setup area in memory."
2201 };
2202 
2203 
2204 /* install */
2205 static int
2206 install_func (char *arg, int flags)
2207 {
2208   char *stage1_file, *dest_dev, *file, *addr;
2209   char *stage1_buffer = (char *) RAW_ADDR (0x100000);
2210   char *stage2_buffer = stage1_buffer + SECTOR_SIZE;
2211   char *old_sect = stage2_buffer + SECTOR_SIZE;
2212   char *stage2_first_buffer = old_sect + SECTOR_SIZE;
2213   char *stage2_second_buffer = stage2_first_buffer + SECTOR_SIZE;
2214   /* XXX: Probably SECTOR_SIZE is reasonable.  */
2215   char *config_filename = stage2_second_buffer + SECTOR_SIZE;
2216   char *dummy = config_filename + SECTOR_SIZE;
2217   int new_drive = GRUB_INVALID_DRIVE;
2218   int dest_drive, dest_partition;
2219   unsigned int dest_sector;
2220   int src_drive, src_partition, src_part_start;
2221   int i;
2222   struct geometry dest_geom, src_geom;
2223   unsigned int saved_sector;
2224   unsigned int stage2_first_sector, stage2_second_sector;
2225   char *ptr;
2226   int installaddr, installlist;
2227   /* Point to the location of the name of a configuration file in Stage 2.  */
2228   char *config_file_location;
2229   /* If FILE is a Stage 1.5?  */
2230   int is_stage1_5 = 0;
2231   /* Must call grub_close?  */
2232   int is_open = 0;
2233   /* If LBA is forced?  */
2234   int is_force_lba = 0;
2235   /* Was the last sector full? */
2236   int last_length = SECTOR_SIZE;
2237 
2238 #ifdef GRUB_UTIL
2239   /* If the Stage 2 is in a partition mounted by an OS, this will store
2240      the filename under the OS.  */
2241   char *stage2_os_file = 0;
2242 #endif /* GRUB_UTIL */
2243 
2244   auto void disk_read_savesect_func (unsigned int sector, int offset,
2245       int length);
2246   auto void disk_read_blocklist_func (unsigned int sector, int offset,
2247       int length);
2248 
2249   /* Save the first sector of Stage2 in STAGE2_SECT.  */
2250   auto void disk_read_savesect_func (unsigned int sector, int offset,
2251       int length)
2252     {
2253       if (debug)
2254 	printf ("[%u]", sector);
2255 
2256       /* ReiserFS has files which sometimes contain data not aligned
2257          on sector boundaries.  Returning an error is better than
2258          silently failing. */
2259       if (offset != 0 || length != SECTOR_SIZE)
2260 	errnum = ERR_UNALIGNED;
2261 
2262       saved_sector = sector;
2263     }
2264 
2265   /* Write SECTOR to INSTALLLIST, and update INSTALLADDR and
2266      INSTALLSECT.  */
2267   auto void disk_read_blocklist_func (unsigned int sector, int offset,
2268       int length)
2269     {
2270       if (debug)
2271 	printf("[%u]", sector);
2272 
2273       if (offset != 0 || last_length != SECTOR_SIZE)
2274 	{
2275 	  /* We found a non-sector-aligned data block. */
2276 	  errnum = ERR_UNALIGNED;
2277 	  return;
2278 	}
2279 
2280       last_length = length;
2281 
2282       if (*((unsigned long *) (installlist - 4))
2283 	  + *((unsigned short *) installlist) != sector
2284 	  || installlist == (int) stage2_first_buffer + SECTOR_SIZE + 4)
2285 	{
2286 	  installlist -= 8;
2287 
2288 	  if (*((unsigned long *) (installlist - 8)))
2289 	    errnum = ERR_WONT_FIT;
2290 	  else
2291 	    {
2292 	      *((unsigned short *) (installlist + 2)) = (installaddr >> 4);
2293 	      *((unsigned long *) (installlist - 4)) = sector;
2294 	    }
2295 	}
2296 
2297       *((unsigned short *) installlist) += 1;
2298       installaddr += 512;
2299     }
2300 
2301   /* First, check the GNU-style long option.  */
2302   while (1)
2303     {
2304       if (grub_memcmp ("--force-lba", arg, sizeof ("--force-lba") - 1) == 0)
2305 	{
2306 	  is_force_lba = 1;
2307 	  arg = skip_to (0, arg);
2308 	}
2309 #ifdef GRUB_UTIL
2310       else if (grub_memcmp ("--stage2=", arg, sizeof ("--stage2=") - 1) == 0)
2311 	{
2312 	  stage2_os_file = arg + sizeof ("--stage2=") - 1;
2313 	  arg = skip_to (0, arg);
2314 	  nul_terminate (stage2_os_file);
2315 	}
2316 #endif /* GRUB_UTIL */
2317       else
2318 	break;
2319     }
2320 
2321   stage1_file = arg;
2322   dest_dev = skip_to (0, stage1_file);
2323   if (*dest_dev == 'd')
2324     {
2325       new_drive = 0;
2326       dest_dev = skip_to (0, dest_dev);
2327     }
2328   file = skip_to (0, dest_dev);
2329   addr = skip_to (0, file);
2330 
2331   /* Get the installation address.  */
2332   if (! safe_parse_maxint (&addr, &installaddr))
2333     {
2334       /* ADDR is not specified.  */
2335       installaddr = 0;
2336       ptr = addr;
2337       errnum = 0;
2338     }
2339   else
2340     ptr = skip_to (0, addr);
2341 
2342 #ifndef NO_DECOMPRESSION
2343   /* Do not decompress Stage 1 or Stage 2.  */
2344   no_decompression = 1;
2345 #endif
2346 
2347   /* Read Stage 1.  */
2348   is_open = grub_open (stage1_file);
2349   if (! is_open
2350       || ! grub_read (stage1_buffer, SECTOR_SIZE) == SECTOR_SIZE)
2351     goto fail;
2352 
2353   /* Read the old sector from DEST_DEV.  */
2354   if (! set_device (dest_dev)
2355       || ! open_partition ()
2356       || ! devread (0, 0, SECTOR_SIZE, old_sect))
2357     goto fail;
2358 
2359   /* Store the information for the destination device.  */
2360   dest_drive = current_drive;
2361   dest_partition = current_partition;
2362   dest_geom = buf_geom;
2363   dest_sector = part_start;
2364 
2365   /* Copy the possible DOS BPB, 59 bytes at byte offset 3.  */
2366   grub_memmove (stage1_buffer + BOOTSEC_BPB_OFFSET,
2367 		old_sect + BOOTSEC_BPB_OFFSET,
2368 		BOOTSEC_BPB_LENGTH);
2369 
2370   /* If for a hard disk, copy the possible MBR/extended part table.  */
2371   if (dest_drive & 0x80)
2372     grub_memmove (stage1_buffer + STAGE1_WINDOWS_NT_MAGIC,
2373 		  old_sect + STAGE1_WINDOWS_NT_MAGIC,
2374 		  STAGE1_PARTEND - STAGE1_WINDOWS_NT_MAGIC);
2375 
2376   /* Check for the version and the signature of Stage 1.  */
2377   if (*((short *)(stage1_buffer + STAGE1_VER_MAJ_OFFS)) != COMPAT_VERSION
2378       || (*((unsigned short *) (stage1_buffer + BOOTSEC_SIG_OFFSET))
2379 	  != BOOTSEC_SIGNATURE))
2380     {
2381       errnum = ERR_BAD_VERSION;
2382       goto fail;
2383     }
2384 
2385   /* This below is not true any longer. But should we leave this alone?  */
2386 
2387   /* If DEST_DRIVE is a floppy, Stage 2 must have the iteration probe
2388      routine.  */
2389   if (! (dest_drive & 0x80)
2390       && (*((unsigned char *) (stage1_buffer + BOOTSEC_PART_OFFSET)) == 0x80
2391 	  || stage1_buffer[BOOTSEC_PART_OFFSET] == 0))
2392     {
2393       errnum = ERR_BAD_VERSION;
2394       goto fail;
2395     }
2396 
2397   grub_close ();
2398 
2399   /* Open Stage 2.  */
2400   is_open = grub_open (file);
2401   if (! is_open)
2402     goto fail;
2403 
2404   src_drive = current_drive;
2405   src_partition = current_partition;
2406   src_part_start = part_start;
2407   src_geom = buf_geom;
2408 
2409   if (! new_drive)
2410     new_drive = src_drive;
2411   else if (src_drive != dest_drive)
2412     grub_printf ("Warning: the option `d' was not used, but the Stage 1 will"
2413 		 " be installed on a\ndifferent drive than the drive where"
2414 		 " the Stage 2 resides.\n");
2415 
2416   /* Set the boot drive.  */
2417   *((unsigned char *) (stage1_buffer + STAGE1_BOOT_DRIVE)) = new_drive;
2418 
2419   /* Set the "force LBA" flag.  */
2420   *((unsigned char *) (stage1_buffer + STAGE1_FORCE_LBA)) = is_force_lba;
2421 
2422   /* If DEST_DRIVE is a hard disk, enable the workaround, which is
2423      for buggy BIOSes which don't pass boot drive correctly. Instead,
2424      they pass 0x00 or 0x01 even when booted from 0x80.  */
2425   if (dest_drive & BIOS_FLAG_FIXED_DISK)
2426     /* Replace the jmp (2 bytes) with double nop's.  */
2427     *((unsigned short *) (stage1_buffer + STAGE1_BOOT_DRIVE_CHECK))
2428       = 0x9090;
2429 
2430   /* Read the first sector of Stage 2.  */
2431   disk_read_hook = disk_read_savesect_func;
2432   if (grub_read (stage2_first_buffer, SECTOR_SIZE) != SECTOR_SIZE)
2433     goto fail;
2434 
2435   stage2_first_sector = saved_sector;
2436 
2437   /* Read the second sector of Stage 2.  */
2438   if (grub_read (stage2_second_buffer, SECTOR_SIZE) != SECTOR_SIZE)
2439     goto fail;
2440 
2441   stage2_second_sector = saved_sector;
2442 
2443   /* Check for the version of Stage 2.  */
2444   if (*((short *) (stage2_second_buffer + STAGE2_VER_MAJ_OFFS))
2445       != COMPAT_VERSION)
2446     {
2447       errnum = ERR_BAD_VERSION;
2448       goto fail;
2449     }
2450 
2451   /* Check for the Stage 2 id.  */
2452   if (stage2_second_buffer[STAGE2_STAGE2_ID] != STAGE2_ID_STAGE2)
2453     is_stage1_5 = 1;
2454 
2455   /* If INSTALLADDR is not specified explicitly in the command-line,
2456      determine it by the Stage 2 id.  */
2457   if (! installaddr)
2458     {
2459       if (! is_stage1_5)
2460 	/* Stage 2.  */
2461 	installaddr = 0x8000;
2462       else
2463 	/* Stage 1.5.  */
2464 	installaddr = 0x2000;
2465     }
2466 
2467   *((unsigned long *) (stage1_buffer + STAGE1_STAGE2_SECTOR))
2468     = stage2_first_sector;
2469   *((unsigned short *) (stage1_buffer + STAGE1_STAGE2_ADDRESS))
2470     = installaddr;
2471   *((unsigned short *) (stage1_buffer + STAGE1_STAGE2_SEGMENT))
2472     = installaddr >> 4;
2473 
2474   i = (int) stage2_first_buffer + SECTOR_SIZE - 4;
2475   while (*((unsigned long *) i))
2476     {
2477       if (i < (int) stage2_first_buffer
2478 	  || (*((int *) (i - 4)) & 0x80000000)
2479 	  || *((unsigned short *) i) >= 0xA00
2480 	  || *((short *) (i + 2)) == 0)
2481 	{
2482 	  errnum = ERR_BAD_VERSION;
2483 	  goto fail;
2484 	}
2485 
2486       *((int *) i) = 0;
2487       *((int *) (i - 4)) = 0;
2488       i -= 8;
2489     }
2490 
2491   installlist = (int) stage2_first_buffer + SECTOR_SIZE + 4;
2492   installaddr += SECTOR_SIZE;
2493 
2494   /* Read the whole of Stage2 except for the first sector.  */
2495   grub_seek (SECTOR_SIZE);
2496 
2497   disk_read_hook = disk_read_blocklist_func;
2498   if (! grub_read (dummy, -1))
2499     goto fail;
2500 
2501   disk_read_hook = 0;
2502 
2503   /* Find a string for the configuration filename.  */
2504   config_file_location = stage2_second_buffer + STAGE2_VER_STR_OFFS;
2505   while (*(config_file_location++))
2506     ;
2507 
2508   /* Set the "force LBA" flag for Stage2.  */
2509   *((unsigned char *) (stage2_second_buffer + STAGE2_FORCE_LBA))
2510     = is_force_lba;
2511 
2512   if (*ptr == 'p')
2513     {
2514       *((long *) (stage2_second_buffer + STAGE2_INSTALLPART))
2515 	= src_partition;
2516       if (is_stage1_5)
2517 	{
2518 	  /* Reset the device information in FILE if it is a Stage 1.5.  */
2519 	  unsigned long device = 0xFFFFFFFF;
2520 
2521 	  grub_memmove (config_file_location, (char *) &device,
2522 			sizeof (device));
2523 	}
2524 
2525       ptr = skip_to (0, ptr);
2526     }
2527 
2528   if (*ptr)
2529     {
2530       grub_strcpy (config_filename, ptr);
2531       nul_terminate (config_filename);
2532 
2533       if (! is_stage1_5)
2534 	/* If it is a Stage 2, just copy PTR to CONFIG_FILE_LOCATION.  */
2535 	grub_strcpy (config_file_location, ptr);
2536       else
2537 	{
2538 	  char *real_config;
2539 	  unsigned long device;
2540 
2541 	  /* Translate the external device syntax to the internal device
2542 	     syntax.  */
2543 	  if (! (real_config = set_device (ptr)))
2544 	    {
2545 	      /* The Stage 2 PTR does not contain the device name, so
2546 		 use the root device instead.  */
2547 	      errnum = ERR_NONE;
2548 	      current_drive = saved_drive;
2549 	      current_partition = saved_partition;
2550 	      real_config = ptr;
2551 	    }
2552 
2553 	  if (current_drive == src_drive)
2554 	    {
2555 	      /* If the drive where the Stage 2 resides is the same as
2556 		 the one where the Stage 1.5 resides, do not embed the
2557 		 drive number.  */
2558 	      current_drive = GRUB_INVALID_DRIVE;
2559 	    }
2560 
2561 	  device = (current_drive << 24) | current_partition;
2562 	  grub_memmove (config_file_location, (char *) &device,
2563 			sizeof (device));
2564 	  grub_strcpy (config_file_location + sizeof (device),
2565 		       real_config);
2566 	}
2567 
2568       /* If a Stage 1.5 is used, then we need to modify the Stage2.  */
2569       if (is_stage1_5)
2570 	{
2571 	  char *real_config_filename = skip_to (0, ptr);
2572 
2573 	  is_open = grub_open (config_filename);
2574 	  if (! is_open)
2575 	    goto fail;
2576 
2577 	  /* Skip the first sector.  */
2578 	  grub_seek (SECTOR_SIZE);
2579 
2580 	  disk_read_hook = disk_read_savesect_func;
2581 	  if (grub_read (stage2_buffer, SECTOR_SIZE) != SECTOR_SIZE)
2582 	    goto fail;
2583 
2584 	  disk_read_hook = 0;
2585 	  grub_close ();
2586 	  is_open = 0;
2587 
2588 	  /* Sanity check.  */
2589 	  if (*(stage2_buffer + STAGE2_STAGE2_ID) != STAGE2_ID_STAGE2)
2590 	    {
2591 	      errnum = ERR_BAD_VERSION;
2592 	      goto fail;
2593 	    }
2594 
2595 	  /* Set the "force LBA" flag for Stage2.  */
2596 	  *(stage2_buffer + STAGE2_FORCE_LBA) = is_force_lba;
2597 
2598 	  /* If REAL_CONFIG_FILENAME is specified, copy it to the Stage2.  */
2599 	  if (*real_config_filename)
2600 	    {
2601 	      /* Specified */
2602 	      char *location;
2603 
2604 	      /* Find a string for the configuration filename.  */
2605 	      location = stage2_buffer + STAGE2_VER_STR_OFFS;
2606 	      while (*(location++))
2607 		;
2608 
2609 	      /* Copy the name.  */
2610 	      grub_strcpy (location, real_config_filename);
2611 	    }
2612 
2613 	  /* Write it to the disk.  */
2614 	  buf_track = BUF_CACHE_INVALID;
2615 
2616 #ifdef GRUB_UTIL
2617 	  /* In the grub shell, access the Stage 2 via the OS filesystem
2618 	     service, if possible.  */
2619 	  if (stage2_os_file)
2620 	    {
2621 	      FILE *fp;
2622 
2623 	      fp = fopen (stage2_os_file, "r+");
2624 	      if (! fp)
2625 		{
2626 		  errnum = ERR_FILE_NOT_FOUND;
2627 		  goto fail;
2628 		}
2629 
2630 	      if (fseek (fp, SECTOR_SIZE, SEEK_SET) != 0)
2631 		{
2632 		  fclose (fp);
2633 		  errnum = ERR_BAD_VERSION;
2634 		  goto fail;
2635 		}
2636 
2637 	      if (fwrite (stage2_buffer, 1, SECTOR_SIZE, fp)
2638 		  != SECTOR_SIZE)
2639 		{
2640 		  fclose (fp);
2641 		  errnum = ERR_WRITE;
2642 		  goto fail;
2643 		}
2644 
2645 	      fclose (fp);
2646 	    }
2647 	  else
2648 #endif /* GRUB_UTIL */
2649 	    {
2650 	      if (! devwrite (saved_sector - part_start, 1, stage2_buffer))
2651 		goto fail;
2652 	    }
2653 	}
2654     }
2655 
2656   /* Clear the cache.  */
2657   buf_track = BUF_CACHE_INVALID;
2658 
2659   /* Write the modified sectors of Stage2 to the disk.  */
2660 #ifdef GRUB_UTIL
2661   if (! is_stage1_5 && stage2_os_file)
2662     {
2663       FILE *fp;
2664 
2665       fp = fopen (stage2_os_file, "r+");
2666       if (! fp)
2667 	{
2668 	  errnum = ERR_FILE_NOT_FOUND;
2669 	  goto fail;
2670 	}
2671 
2672       if (fwrite (stage2_first_buffer, 1, SECTOR_SIZE, fp) != SECTOR_SIZE)
2673 	{
2674 	  fclose (fp);
2675 	  errnum = ERR_WRITE;
2676 	  goto fail;
2677 	}
2678 
2679       if (fwrite (stage2_second_buffer, 1, SECTOR_SIZE, fp) != SECTOR_SIZE)
2680 	{
2681 	  fclose (fp);
2682 	  errnum = ERR_WRITE;
2683 	  goto fail;
2684 	}
2685 
2686       fclose (fp);
2687     }
2688   else
2689 #endif /* GRUB_UTIL */
2690     {
2691       /* The first.  */
2692       current_drive = src_drive;
2693       current_partition = src_partition;
2694 
2695       if (! open_partition ())
2696 	goto fail;
2697 
2698       if (! devwrite (stage2_first_sector - src_part_start, 1,
2699 		      stage2_first_buffer))
2700 	goto fail;
2701 
2702       if (! devwrite (stage2_second_sector - src_part_start, 1,
2703 		      stage2_second_buffer))
2704 	goto fail;
2705     }
2706 
2707   /* Write the modified sector of Stage 1 to the disk.  */
2708   current_drive = dest_drive;
2709   current_partition = dest_partition;
2710   if (! open_partition ())
2711     goto fail;
2712 
2713   devwrite (0, 1, stage1_buffer);
2714 
2715  fail:
2716   if (is_open)
2717     grub_close ();
2718 
2719   disk_read_hook = 0;
2720 
2721 #ifndef NO_DECOMPRESSION
2722   no_decompression = 0;
2723 #endif
2724 
2725   return errnum;
2726 }
2727 
2728 static struct builtin builtin_install =
2729 {
2730   "install",
2731   install_func,
2732   BUILTIN_CMDLINE,
2733   "install [--stage2=STAGE2_FILE] [--force-lba] STAGE1 [d] DEVICE STAGE2 [ADDR] [p] [CONFIG_FILE] [REAL_CONFIG_FILE]",
2734   "Install STAGE1 on DEVICE, and install a blocklist for loading STAGE2"
2735   " as a Stage 2. If the option `d' is present, the Stage 1 will always"
2736   " look for the disk where STAGE2 was installed, rather than using"
2737   " the booting drive. The Stage 2 will be loaded at address ADDR, which"
2738   " will be determined automatically if you don't specify it. If"
2739   " the option `p' or CONFIG_FILE is present, then the first block"
2740   " of Stage 2 is patched with new values of the partition and name"
2741   " of the configuration file used by the true Stage 2 (for a Stage 1.5,"
2742   " this is the name of the true Stage 2) at boot time. If STAGE2 is a Stage"
2743   " 1.5 and REAL_CONFIG_FILE is present, then the Stage 2 CONFIG_FILE is"
2744   " patched with the configuration filename REAL_CONFIG_FILE."
2745   " If the option `--force-lba' is specified, disable some sanity checks"
2746   " for LBA mode. If the option `--stage2' is specified, rewrite the Stage"
2747   " 2 via your OS's filesystem instead of the raw device."
2748 };
2749 
2750 
2751 /* ioprobe */
2752 static int
2753 ioprobe_func (char *arg, int flags)
2754 {
2755 #ifdef GRUB_UTIL
2756 
2757   errnum = ERR_UNRECOGNIZED;
2758   return 1;
2759 
2760 #else /* ! GRUB_UTIL */
2761 
2762   unsigned short *port;
2763 
2764   /* Get the drive number.  */
2765   set_device (arg);
2766   if (errnum)
2767     return 1;
2768 
2769   /* Clean out IO_MAP.  */
2770   grub_memset ((char *) io_map, 0, IO_MAP_SIZE * sizeof (unsigned short));
2771 
2772   /* Track the int13 handler.  */
2773   track_int13 (current_drive);
2774 
2775   /* Print out the result.  */
2776   for (port = io_map; *port != 0; port++)
2777     grub_printf (" 0x%x", (unsigned int) *port);
2778 
2779   return 0;
2780 
2781 #endif /* ! GRUB_UTIL */
2782 }
2783 
2784 static struct builtin builtin_ioprobe =
2785 {
2786   "ioprobe",
2787   ioprobe_func,
2788   BUILTIN_CMDLINE,
2789   "ioprobe DRIVE",
2790   "Probe I/O ports used for the drive DRIVE."
2791 };
2792 
2793 
2794 /*
2795  * To boot from a ZFS root filesystem, the kernel$ or module$ commands
2796  * must include "-B $ZFS-BOOTFS" to expand to the zfs-bootfs, bootpath,
2797  * and diskdevid boot property values for passing to the kernel:
2798  *
2799  * e.g.
2800  * kernel$ /platform/i86pc/kernel/$ISADIR/unix -B $ZFS-BOOTFS,console=ttya
2801  *
2802  * $ZFS-BOOTFS is expanded to
2803  *
2804  *    zfs-bootfs=<rootpool-name/zfs-rootfilesystem-object-num>,
2805  *    bootpath=<device phys path>,
2806  *    diskdevid=<device id>
2807  *
2808  * if both bootpath and diskdevid can be found.
2809  * e.g
2810  *    zfs-bootfs=rpool/85,
2811  *    bootpath="/pci@0,0/pci1022,7450@a/pci17c2,10@4/sd@0,0:a",
2812  *    diskdevid="id1,sd@SSEAGATE_ST336607LC______3JA0LNHE0000741326W6/a"
2813  */
2814 static int
2815 expand_dollar_bootfs(char *in, char *out)
2816 {
2817 	char *token, *tmpout = out;
2818 	int outlen, blen;
2819 	int postcomma = 0;
2820 
2821 	/* no op if this is not zfs */
2822 	if (is_zfs_mount == 0)
2823 		return (0);
2824 
2825 	if (current_bootpath[0] == '\0' && current_devid[0] == '\0') {
2826 		errnum = ERR_NO_BOOTPATH;
2827 		return (1);
2828 	}
2829 
2830 	outlen = strlen(in);
2831 	blen = current_bootfs_obj == 0 ? strlen(current_rootpool) :
2832 	    strlen(current_rootpool) + 11;
2833 
2834 	out[0] = '\0';
2835 	while (token = strstr(in, "$ZFS-BOOTFS")) {
2836 
2837 		if ((outlen += blen) >= MAX_CMDLINE) {
2838 			errnum = ERR_WONT_FIT;
2839 			return (1);
2840 		}
2841 
2842 		token[0] = '\0';
2843 		grub_sprintf(tmpout, "%s", in);
2844 		token[0] = '$';
2845 		in = token + 11; /* skip over $ZFS-BOOTFS */
2846 		tmpout = out + strlen(out);
2847 
2848 		/* Note: %u only fits 32 bit integer; */
2849 		if (current_bootfs_obj > 0)
2850 			grub_sprintf(tmpout, "zfs-bootfs=%s/%u",
2851 			    current_rootpool, current_bootfs_obj);
2852 		else
2853 			grub_sprintf(tmpout, "zfs-bootfs=%s",
2854 			    current_rootpool);
2855 		tmpout = out + strlen(out);
2856 	}
2857 
2858 	/*
2859 	 * Check to see if 'zfs-bootfs' was explicitly specified on the command
2860 	 * line so that we can insert the 'bootpath' property.
2861 	 */
2862 	if ((tmpout == out) && (token = strstr(in, "zfs-bootfs")) != NULL) {
2863 		token[0] = '\0';
2864 		grub_strcpy(tmpout, in);
2865 		token[0] = 'z';
2866 		in = token;
2867 
2868 		tmpout = out + strlen(out);
2869 		postcomma = 1;
2870 	}
2871 
2872 	/*
2873 	 * Set the 'bootpath' property if a ZFS dataset was specified, either
2874 	 * through '$ZFS-BOOTFS' or an explicit 'zfs-bootfs' setting.
2875 	 */
2876 	if (tmpout != out) {
2877 		if (current_bootpath[0] != '\0') {
2878 			if ((outlen += 12 + strlen(current_bootpath))
2879 			    >= MAX_CMDLINE) {
2880 				errnum = ERR_WONT_FIT;
2881 				return (1);
2882 			}
2883 			grub_sprintf(tmpout,
2884 			    postcomma ? "bootpath=\"%s\"," : ",bootpath=\"%s\"",
2885 			    current_bootpath);
2886 			tmpout = out + strlen(out);
2887 		}
2888 
2889 		if (current_devid[0] != '\0') {
2890 			if ((outlen += 13 + strlen(current_devid))
2891 			    >= MAX_CMDLINE) {
2892 				errnum = ERR_WONT_FIT;
2893 				return (1);
2894 			}
2895 			grub_sprintf(tmpout,
2896 			    postcomma ? "diskdevid=\"%s\"," : ",diskdevid=\"%s\"",
2897 			    current_devid);
2898 		}
2899 	}
2900 
2901 	strncat(out, in, MAX_CMDLINE);
2902 	return (0);
2903 }
2904 
2905 /* kernel */
2906 static int
2907 kernel_func (char *arg, int flags)
2908 {
2909   int len;
2910   kernel_t suggested_type = KERNEL_TYPE_NONE;
2911   unsigned long load_flags = 0;
2912 
2913 #ifndef AUTO_LINUX_MEM_OPT
2914   load_flags |= KERNEL_LOAD_NO_MEM_OPTION;
2915 #endif
2916 
2917   /* Deal with GNU-style long options.  */
2918   while (1)
2919     {
2920       /* If the option `--type=TYPE' is specified, convert the string to
2921 	 a kernel type.  */
2922       if (grub_memcmp (arg, "--type=", 7) == 0)
2923 	{
2924 	  arg += 7;
2925 
2926 	  if (grub_memcmp (arg, "netbsd", 6) == 0)
2927 	    suggested_type = KERNEL_TYPE_NETBSD;
2928 	  else if (grub_memcmp (arg, "freebsd", 7) == 0)
2929 	    suggested_type = KERNEL_TYPE_FREEBSD;
2930 	  else if (grub_memcmp (arg, "openbsd", 7) == 0)
2931 	    /* XXX: For now, OpenBSD is identical to NetBSD, from GRUB's
2932 	       point of view.  */
2933 	    suggested_type = KERNEL_TYPE_NETBSD;
2934 	  else if (grub_memcmp (arg, "linux", 5) == 0)
2935 	    suggested_type = KERNEL_TYPE_LINUX;
2936 	  else if (grub_memcmp (arg, "biglinux", 8) == 0)
2937 	    suggested_type = KERNEL_TYPE_BIG_LINUX;
2938 	  else if (grub_memcmp (arg, "multiboot", 9) == 0)
2939 	    suggested_type = KERNEL_TYPE_MULTIBOOT;
2940 	  else
2941 	    {
2942 	      errnum = ERR_BAD_ARGUMENT;
2943 	      return 1;
2944 	    }
2945 	}
2946       /* If the `--no-mem-option' is specified, don't pass a Linux's mem
2947 	 option automatically. If the kernel is another type, this flag
2948 	 has no effect.  */
2949       else if (grub_memcmp (arg, "--no-mem-option", 15) == 0)
2950 	load_flags |= KERNEL_LOAD_NO_MEM_OPTION;
2951       else
2952 	break;
2953 
2954       /* Try the next.  */
2955       arg = skip_to (0, arg);
2956     }
2957 
2958   len = grub_strlen (arg);
2959 
2960   /* Reset MB_CMDLINE.  */
2961   mb_cmdline = (char *) MB_CMDLINE_BUF;
2962   if (len + 1 > MB_CMDLINE_BUFLEN)
2963     {
2964       errnum = ERR_WONT_FIT;
2965       return 1;
2966     }
2967 
2968   /* Copy the command-line to MB_CMDLINE.  */
2969   grub_memmove (mb_cmdline, arg, len + 1);
2970   kernel_type = load_image (arg, mb_cmdline, suggested_type, load_flags);
2971   if (kernel_type == KERNEL_TYPE_NONE)
2972     return 1;
2973 
2974   mb_cmdline += grub_strlen(mb_cmdline) + 1;
2975   return 0;
2976 }
2977 
2978 static struct builtin builtin_kernel =
2979 {
2980   "kernel",
2981   kernel_func,
2982   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
2983   "kernel [--no-mem-option] [--type=TYPE] FILE [ARG ...]",
2984   "Attempt to load the primary boot image from FILE. The rest of the"
2985   " line is passed verbatim as the \"kernel command line\".  Any modules"
2986   " must be reloaded after using this command. The option --type is used"
2987   " to suggest what type of kernel to be loaded. TYPE must be either of"
2988   " \"netbsd\", \"freebsd\", \"openbsd\", \"linux\", \"biglinux\" and"
2989   " \"multiboot\". The option --no-mem-option tells GRUB not to pass a"
2990   " Linux's mem option automatically."
2991 };
2992 
2993 int
2994 min_mem64_func(char *arg, int flags)
2995 {
2996 	if (!safe_parse_maxint(&arg, &min_mem64))
2997 		return (1);
2998 }
2999 
3000 static struct builtin builtin_min_mem64 =
3001 {
3002 	"min_mem64",
3003 	min_mem64_func,
3004 	BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_SCRIPT | BUILTIN_HELP_LIST,
3005 	"min_mem64 <memory in MB>",
3006 	"Sets minimum memory (in MB) required for $ISADIR to expand to amd64, "
3007 	"even on 64-bit capable hardware."
3008 };
3009 
3010 int
3011 check_min_mem64()
3012 {
3013 	if (min_mem64 == 0)
3014 		return (1);
3015 
3016 	if ((mbi.mem_upper / 10240) * 11 >= min_mem64)
3017 		return (1);
3018 
3019 	return (0);
3020 }
3021 
3022 static int detect_target_operating_mode();
3023 
3024 int
3025 amd64_config_cpu(void)
3026 {
3027         struct amd64_cpuid_regs __vcr, *vcr = &__vcr;
3028         uint32_t maxeax;
3029         uint32_t max_maxeax = 0x100;
3030         char vendor[13];
3031         int isamd64 = 0;
3032         uint32_t stdfeatures = 0, xtdfeatures = 0;
3033         uint64_t efer;
3034 
3035         /*
3036          * This check may seem silly, but if the C preprocesor symbol __amd64
3037          * is #defined during compilation, something that may outwardly seem
3038          * like a good idea, uts/common/sys/isa_defs.h will #define _LP64,
3039          * which will cause uts/common/sys/int_types.h to typedef uint64_t as
3040          * an unsigned long - which is only 4 bytes in size when using a 32-bit
3041          * compiler.
3042          *
3043          * If that happens, all the page table translation routines will fail
3044          * horribly, so check the size of uint64_t just to insure some degree
3045          * of sanity in future operations.
3046          */
3047         /*LINTED [sizeof result is invarient]*/
3048         if (sizeof (uint64_t) != 8)
3049                 prom_panic("grub compiled improperly, unable to boot "
3050                     "64-bit AMD64 executables");
3051 
3052         /*
3053          * If the CPU doesn't support the CPUID instruction, it's definitely
3054          * not an AMD64.
3055          */
3056         if (amd64_cpuid_supported() == 0)
3057                 return (0);
3058 
3059         amd64_cpuid_insn(0, vcr);
3060 
3061         maxeax = vcr->r_eax;
3062         {
3063                 /*LINTED [vendor string from cpuid data]*/
3064                 uint32_t *iptr = (uint32_t *)vendor;
3065 
3066                 *iptr++ = vcr->r_ebx;
3067                 *iptr++ = vcr->r_edx;
3068                 *iptr++ = vcr->r_ecx;
3069 
3070                 vendor[12] = '\0';
3071         }
3072 
3073         if (maxeax > max_maxeax) {
3074                 grub_printf("cpu: warning, maxeax was 0x%x -> 0x%x\n",
3075                     maxeax, max_maxeax);
3076                 maxeax = max_maxeax;
3077         }
3078 
3079         if (maxeax < 1)
3080                 return (0);     /* no additional functions, not an AMD64 */
3081         else {
3082                 uint_t family, model, step;
3083 
3084                 amd64_cpuid_insn(1, vcr);
3085 
3086                 /*
3087                  * All AMD64/IA32e processors technically SHOULD report
3088                  * themselves as being in family 0xf, but for some reason
3089                  * Simics doesn't, and this may change in the future, so
3090                  * don't error out if it's not true.
3091                  */
3092                 if ((family = BITX(vcr->r_eax, 11, 8)) == 0xf)
3093                         family += BITX(vcr->r_eax, 27, 20);
3094 
3095                 if ((model = BITX(vcr->r_eax, 7, 4)) == 0xf)
3096                         model += BITX(vcr->r_eax, 19, 16) << 4;
3097                 step = BITX(vcr->r_eax, 3, 0);
3098 
3099                 grub_printf("cpu: '%s' family %d model %d step %d\n",
3100                     vendor, family, model, step);
3101                 stdfeatures = vcr->r_edx;
3102         }
3103 
3104         amd64_cpuid_insn(0x80000000, vcr);
3105 
3106         if (vcr->r_eax & 0x80000000) {
3107                 uint32_t xmaxeax = vcr->r_eax;
3108                 const uint32_t max_xmaxeax = 0x80000100;
3109 
3110                 if (xmaxeax > max_xmaxeax) {
3111                         grub_printf("amd64: warning, xmaxeax was "
3112 			    "0x%x -> 0x%x\n", xmaxeax, max_xmaxeax);
3113                         xmaxeax = max_xmaxeax;
3114                 }
3115 
3116                 if (xmaxeax >= 0x80000001) {
3117                         amd64_cpuid_insn(0x80000001, vcr);
3118                         xtdfeatures = vcr->r_edx;
3119                 }
3120         }
3121 
3122         if (BITX(xtdfeatures, 29, 29))          /* long mode */
3123                 isamd64++;
3124         else
3125                 grub_printf("amd64: CPU does NOT support long mode\n");
3126 
3127         if (!BITX(stdfeatures, 0, 0)) {
3128                 grub_printf("amd64: CPU does NOT support FPU\n");
3129                 isamd64--;
3130         }
3131 
3132         if (!BITX(stdfeatures, 4, 4)) {
3133                 grub_printf("amd64: CPU does NOT support TSC\n");
3134                 isamd64--;
3135         }
3136 
3137         if (!BITX(stdfeatures, 5, 5)) {
3138                 grub_printf("amd64: CPU does NOT support MSRs\n");
3139                 isamd64--;
3140         }
3141 
3142         if (!BITX(stdfeatures, 6, 6)) {
3143                 grub_printf("amd64: CPU does NOT support PAE\n");
3144                 isamd64--;
3145         }
3146 
3147         if (!BITX(stdfeatures, 8, 8)) {
3148                 grub_printf("amd64: CPU does NOT support CX8\n");
3149                 isamd64--;
3150         }
3151 
3152         if (!BITX(stdfeatures, 13, 13)) {
3153                 grub_printf("amd64: CPU does NOT support PGE\n");
3154                 isamd64--;
3155         }
3156 
3157         if (!BITX(stdfeatures, 19, 19)) {
3158                 grub_printf("amd64: CPU does NOT support CLFSH\n");
3159                 isamd64--;
3160         }
3161 
3162         if (!BITX(stdfeatures, 23, 23)) {
3163                 grub_printf("amd64: CPU does NOT support MMX\n");
3164                 isamd64--;
3165         }
3166 
3167         if (!BITX(stdfeatures, 24, 24)) {
3168                 grub_printf("amd64: CPU does NOT support FXSR\n");
3169                 isamd64--;
3170         }
3171 
3172         if (!BITX(stdfeatures, 25, 25)) {
3173                 grub_printf("amd64: CPU does NOT support SSE\n");
3174                 isamd64--;
3175         }
3176 
3177         if (!BITX(stdfeatures, 26, 26)) {
3178                 grub_printf("amd64: CPU does NOT support SSE2\n");
3179                 isamd64--;
3180         }
3181 
3182         if (isamd64 < 1) {
3183                 grub_printf("amd64: CPU does not support amd64 executables.\n");
3184                 return (0);
3185         }
3186 
3187         amd64_rdmsr(MSR_AMD_EFER, &efer);
3188         if (efer & AMD_EFER_SCE)
3189                 grub_printf("amd64: EFER_SCE (syscall/sysret) already "
3190 		    "enabled\n");
3191         if (efer & AMD_EFER_NXE)
3192                 grub_printf("amd64: EFER_NXE (no-exec prot) already enabled\n");
3193         if (efer & AMD_EFER_LME)
3194                 grub_printf("amd64: EFER_LME (long mode) already enabled\n");
3195 
3196         return (detect_target_operating_mode());
3197 }
3198 
3199 static int
3200 detect_target_operating_mode()
3201 {
3202         int ret, ah;
3203 
3204 	ah = get_target_operating_mode();
3205 
3206         ah = ah >> 8;
3207 
3208 	/* XXX still need to pass back the return from the call  */
3209 	ret = 0;
3210 
3211         if (ah == 0x86 && (ret & CB) != 0) {
3212                 grub_printf("[BIOS 'Detect Target Operating Mode' "
3213                     "callback unsupported on this platform]\n");
3214                 return (1);     /* unsupported, ignore */
3215         }
3216 
3217         if (ah == 0x0 && (ret & CB) == 0) {
3218                 grub_printf("[BIOS accepted mixed-mode target setting!]\n");
3219                 return (1);     /* told the bios what we're up to */
3220         }
3221 
3222         if (ah == 0 && ret & CB) {
3223                 grub_printf("fatal: BIOS reports this machine CANNOT run in "
3224 		    "mixed 32/64-bit mode!\n");
3225                 return (0);
3226         }
3227 
3228         grub_printf("warning: BIOS Detect Target Operating Mode callback "
3229             "confused.\n         %%ax >> 8 = 0x%x, carry = %d\n", ah,
3230             ret & CB ? 1 : 0);
3231 
3232         return (1);
3233 }
3234 
3235 
3236 int
3237 isamd64()
3238 {
3239 	static int ret = -1;
3240 
3241 	if (ret == -1)
3242 		ret = amd64_config_cpu();
3243 
3244 	return (ret);
3245 }
3246 
3247 static void
3248 expand_arch (char *arg, char *newarg)
3249 {
3250   char *index;
3251 
3252   newarg[0] = '\0';
3253 
3254   while ((index = strstr(arg, "$ISADIR")) != NULL) {
3255 
3256     index[0] = '\0';
3257     strncat(newarg, arg, MAX_CMDLINE);
3258     index[0] = '$';
3259 
3260     if (isamd64() && check_min_mem64())
3261       strncat(newarg, "amd64", MAX_CMDLINE);
3262 
3263     arg = index + 7;
3264   }
3265 
3266   strncat(newarg, arg, MAX_CMDLINE);
3267   return;
3268 }
3269 
3270 /* kernel$ */
3271 static int
3272 kernel_dollar_func (char *arg, int flags)
3273 {
3274   char newarg[MAX_CMDLINE];	/* everything boils down to MAX_CMDLINE */
3275 
3276   grub_printf("loading '%s' ...\n", arg);
3277   expand_arch(arg, newarg);
3278 
3279   if (kernel_func(newarg, flags))
3280 	return (1);
3281 
3282   mb_cmdline = (char *)MB_CMDLINE_BUF;
3283   if (expand_dollar_bootfs(newarg, mb_cmdline)) {
3284 	grub_printf("cannot expand $ZFS-BOOTFS for dataset %s\n",
3285 	    current_bootfs);
3286 	return (1);
3287   }
3288 
3289   grub_printf("'%s' is loaded\n", mb_cmdline);
3290   mb_cmdline += grub_strlen(mb_cmdline) + 1;
3291 
3292   return (0);
3293 }
3294 
3295 static struct builtin builtin_kernel_dollar =
3296 {
3297   "kernel$",
3298   kernel_dollar_func,
3299   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3300   "kernel$ [--no-mem-option] [--type=TYPE] FILE [ARG ...]",
3301   " Just like kernel, but with $ISADIR expansion."
3302 };
3303 
3304 
3305 /* lock */
3306 static int
3307 lock_func (char *arg, int flags)
3308 {
3309   if (! auth && password)
3310     {
3311       errnum = ERR_PRIVILEGED;
3312       return 1;
3313     }
3314 
3315   return 0;
3316 }
3317 
3318 static struct builtin builtin_lock =
3319 {
3320   "lock",
3321   lock_func,
3322   BUILTIN_CMDLINE,
3323   "lock",
3324   "Break a command execution unless the user is authenticated."
3325 };
3326 
3327 
3328 /* makeactive */
3329 static int
3330 makeactive_func (char *arg, int flags)
3331 {
3332   if (! make_saved_active ())
3333     return 1;
3334 
3335   return 0;
3336 }
3337 
3338 static struct builtin builtin_makeactive =
3339 {
3340   "makeactive",
3341   makeactive_func,
3342   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3343   "makeactive",
3344   "Set the active partition on the root disk to GRUB's root device."
3345   " This command is limited to _primary_ PC partitions on a hard disk."
3346 };
3347 
3348 
3349 /* map */
3350 /* Map FROM_DRIVE to TO_DRIVE.  */
3351 static int
3352 map_func (char *arg, int flags)
3353 {
3354   char *to_drive;
3355   char *from_drive;
3356   unsigned long to, from;
3357   int i;
3358 
3359   to_drive = arg;
3360   from_drive = skip_to (0, arg);
3361 
3362   /* Get the drive number for TO_DRIVE.  */
3363   set_device (to_drive);
3364   if (errnum)
3365     return 1;
3366   to = current_drive;
3367 
3368   /* Get the drive number for FROM_DRIVE.  */
3369   set_device (from_drive);
3370   if (errnum)
3371     return 1;
3372   from = current_drive;
3373 
3374   /* Search for an empty slot in BIOS_DRIVE_MAP.  */
3375   for (i = 0; i < DRIVE_MAP_SIZE; i++)
3376     {
3377       /* Perhaps the user wants to override the map.  */
3378       if ((bios_drive_map[i] & 0xff) == from)
3379 	break;
3380 
3381       if (! bios_drive_map[i])
3382 	break;
3383     }
3384 
3385   if (i == DRIVE_MAP_SIZE)
3386     {
3387       errnum = ERR_WONT_FIT;
3388       return 1;
3389     }
3390 
3391   if (to == from)
3392     /* If TO is equal to FROM, delete the entry.  */
3393     grub_memmove ((char *) &bios_drive_map[i], (char *) &bios_drive_map[i + 1],
3394 		  sizeof (unsigned short) * (DRIVE_MAP_SIZE - i));
3395   else
3396     bios_drive_map[i] = from | (to << 8);
3397 
3398   return 0;
3399 }
3400 
3401 static struct builtin builtin_map =
3402 {
3403   "map",
3404   map_func,
3405   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3406   "map TO_DRIVE FROM_DRIVE",
3407   "Map the drive FROM_DRIVE to the drive TO_DRIVE. This is necessary"
3408   " when you chain-load some operating systems, such as DOS, if such an"
3409   " OS resides at a non-first drive."
3410 };
3411 
3412 
3413 #ifdef USE_MD5_PASSWORDS
3414 /* md5crypt */
3415 static int
3416 md5crypt_func (char *arg, int flags)
3417 {
3418   char crypted[36];
3419   char key[32];
3420   unsigned int seed;
3421   int i;
3422   const char *const seedchars =
3423     "./0123456789ABCDEFGHIJKLMNOPQRST"
3424     "UVWXYZabcdefghijklmnopqrstuvwxyz";
3425 
3426   /* First create a salt.  */
3427 
3428   /* The magical prefix.  */
3429   grub_memset (crypted, 0, sizeof (crypted));
3430   grub_memmove (crypted, "$1$", 3);
3431 
3432   /* Create the length of a salt.  */
3433   seed = currticks ();
3434 
3435   /* Generate a salt.  */
3436   for (i = 0; i < 8 && seed; i++)
3437     {
3438       /* FIXME: This should be more random.  */
3439       crypted[3 + i] = seedchars[seed & 0x3f];
3440       seed >>= 6;
3441     }
3442 
3443   /* A salt must be terminated with `$', if it is less than 8 chars.  */
3444   crypted[3 + i] = '$';
3445 
3446 #ifdef DEBUG_MD5CRYPT
3447   grub_printf ("salt = %s\n", crypted);
3448 #endif
3449 
3450   /* Get a password.  */
3451   grub_memset (key, 0, sizeof (key));
3452   get_cmdline ("Password: ", key, sizeof (key) - 1, '*', 0);
3453 
3454   /* Crypt the key.  */
3455   make_md5_password (key, crypted);
3456 
3457   grub_printf ("Encrypted: %s\n", crypted);
3458   return 0;
3459 }
3460 
3461 static struct builtin builtin_md5crypt =
3462 {
3463   "md5crypt",
3464   md5crypt_func,
3465   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3466   "md5crypt",
3467   "Generate a password in MD5 format."
3468 };
3469 #endif /* USE_MD5_PASSWORDS */
3470 
3471 
3472 /* module */
3473 static int
3474 module_func (char *arg, int flags)
3475 {
3476   int len = grub_strlen (arg);
3477 
3478   switch (kernel_type)
3479     {
3480     case KERNEL_TYPE_MULTIBOOT:
3481       if (mb_cmdline + len + 1 > (char *) MB_CMDLINE_BUF + MB_CMDLINE_BUFLEN)
3482 	{
3483 	  errnum = ERR_WONT_FIT;
3484 	  return 1;
3485 	}
3486       grub_memmove (mb_cmdline, arg, len + 1);
3487       if (! load_module (arg, mb_cmdline))
3488 	return 1;
3489 
3490       mb_cmdline += grub_strlen(mb_cmdline) + 1;
3491       break;
3492 
3493     case KERNEL_TYPE_LINUX:
3494     case KERNEL_TYPE_BIG_LINUX:
3495       if (! load_initrd (arg))
3496 	return 1;
3497       break;
3498 
3499     default:
3500       errnum = ERR_NEED_MB_KERNEL;
3501       return 1;
3502     }
3503 
3504   return 0;
3505 }
3506 
3507 static struct builtin builtin_module =
3508 {
3509   "module",
3510   module_func,
3511   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3512   "module FILE [ARG ...]",
3513   "Load a boot module FILE for a Multiboot format boot image (no"
3514   " interpretation of the file contents is made, so users of this"
3515   " command must know what the kernel in question expects). The"
3516   " rest of the line is passed as the \"module command line\", like"
3517   " the `kernel' command."
3518 };
3519 
3520 /* module$ */
3521 static int
3522 module_dollar_func (char *arg, int flags)
3523 {
3524   char newarg[MAX_CMDLINE];	/* everything boils down to MAX_CMDLINE */
3525   char *cmdline_sav;
3526 
3527   grub_printf("loading '%s' ...\n", arg);
3528   expand_arch(arg, newarg);
3529 
3530   cmdline_sav = (char *)mb_cmdline;
3531   if (module_func(newarg, flags))
3532 	return (1);
3533 
3534   if (expand_dollar_bootfs(newarg, cmdline_sav)) {
3535 	grub_printf("cannot expand $ZFS-BOOTFS for dataset %s\n",
3536 	    current_bootfs);
3537 	return (1);
3538   }
3539 
3540   grub_printf("'%s' is loaded\n", (char *)cmdline_sav);
3541   mb_cmdline += grub_strlen(cmdline_sav) + 1;
3542 
3543   return (0);
3544 }
3545 
3546 static struct builtin builtin_module_dollar =
3547 {
3548   "module$",
3549   module_dollar_func,
3550   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3551   "module FILE [ARG ...]",
3552   " Just like module, but with $ISADIR expansion."
3553 };
3554 
3555 
3556 /* modulenounzip */
3557 static int
3558 modulenounzip_func (char *arg, int flags)
3559 {
3560   int ret;
3561 
3562 #ifndef NO_DECOMPRESSION
3563   no_decompression = 1;
3564 #endif
3565 
3566   ret = module_func (arg, flags);
3567 
3568 #ifndef NO_DECOMPRESSION
3569   no_decompression = 0;
3570 #endif
3571 
3572   return ret;
3573 }
3574 
3575 static struct builtin builtin_modulenounzip =
3576 {
3577   "modulenounzip",
3578   modulenounzip_func,
3579   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3580   "modulenounzip FILE [ARG ...]",
3581   "The same as `module', except that automatic decompression is"
3582   " disabled."
3583 };
3584 
3585 
3586 /* pager [on|off] */
3587 static int
3588 pager_func (char *arg, int flags)
3589 {
3590   /* If ARG is empty, toggle the flag.  */
3591   if (! *arg)
3592     use_pager = ! use_pager;
3593   else if (grub_memcmp (arg, "on", 2) == 0)
3594     use_pager = 1;
3595   else if (grub_memcmp (arg, "off", 3) == 0)
3596     use_pager = 0;
3597   else
3598     {
3599       errnum = ERR_BAD_ARGUMENT;
3600       return 1;
3601     }
3602 
3603   grub_printf (" Internal pager is now %s\n", use_pager ? "on" : "off");
3604   return 0;
3605 }
3606 
3607 static struct builtin builtin_pager =
3608 {
3609   "pager",
3610   pager_func,
3611   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
3612   "pager [FLAG]",
3613   "Toggle pager mode with no argument. If FLAG is given and its value"
3614   " is `on', turn on the mode. If FLAG is `off', turn off the mode."
3615 };
3616 
3617 
3618 /* partnew PART TYPE START LEN */
3619 static int
3620 partnew_func (char *arg, int flags)
3621 {
3622   int new_type, new_start, new_len;
3623   int start_cl, start_ch, start_dh;
3624   int end_cl, end_ch, end_dh;
3625   int entry;
3626   char mbr[512];
3627 
3628   /* Convert a LBA address to a CHS address in the INT 13 format.  */
3629   auto void lba_to_chs (int lba, int *cl, int *ch, int *dh);
3630   void lba_to_chs (int lba, int *cl, int *ch, int *dh)
3631     {
3632       int cylinder, head, sector;
3633 
3634       sector = lba % buf_geom.sectors + 1;
3635       head = (lba / buf_geom.sectors) % buf_geom.heads;
3636       cylinder = lba / (buf_geom.sectors * buf_geom.heads);
3637 
3638       if (cylinder >= buf_geom.cylinders)
3639 	cylinder = buf_geom.cylinders - 1;
3640 
3641       *cl = sector | ((cylinder & 0x300) >> 2);
3642       *ch = cylinder & 0xFF;
3643       *dh = head;
3644     }
3645 
3646   /* Get the drive and the partition.  */
3647   if (! set_device (arg))
3648     return 1;
3649 
3650   /* The drive must be a hard disk.  */
3651   if (! (current_drive & 0x80))
3652     {
3653       errnum = ERR_BAD_ARGUMENT;
3654       return 1;
3655     }
3656 
3657   /* The partition must a primary partition.  */
3658   if ((current_partition >> 16) > 3
3659       || (current_partition & 0xFFFF) != 0xFFFF)
3660     {
3661       errnum = ERR_BAD_ARGUMENT;
3662       return 1;
3663     }
3664 
3665   entry = current_partition >> 16;
3666 
3667   /* Get the new partition type.  */
3668   arg = skip_to (0, arg);
3669   if (! safe_parse_maxint (&arg, &new_type))
3670     return 1;
3671 
3672   /* The partition type is unsigned char.  */
3673   if (new_type > 0xFF)
3674     {
3675       errnum = ERR_BAD_ARGUMENT;
3676       return 1;
3677     }
3678 
3679   /* Get the new partition start.  */
3680   arg = skip_to (0, arg);
3681   if (! safe_parse_maxint (&arg, &new_start))
3682     return 1;
3683 
3684   /* Get the new partition length.  */
3685   arg = skip_to (0, arg);
3686   if (! safe_parse_maxint (&arg, &new_len))
3687     return 1;
3688 
3689   /* Read the MBR.  */
3690   if (! rawread (current_drive, 0, 0, SECTOR_SIZE, mbr))
3691     return 1;
3692 
3693   /* Store the partition information in the MBR.  */
3694   lba_to_chs (new_start, &start_cl, &start_ch, &start_dh);
3695   lba_to_chs (new_start + new_len - 1, &end_cl, &end_ch, &end_dh);
3696 
3697   PC_SLICE_FLAG (mbr, entry) = 0;
3698   PC_SLICE_HEAD (mbr, entry) = start_dh;
3699   PC_SLICE_SEC (mbr, entry) = start_cl;
3700   PC_SLICE_CYL (mbr, entry) = start_ch;
3701   PC_SLICE_TYPE (mbr, entry) = new_type;
3702   PC_SLICE_EHEAD (mbr, entry) = end_dh;
3703   PC_SLICE_ESEC (mbr, entry) = end_cl;
3704   PC_SLICE_ECYL (mbr, entry) = end_ch;
3705   PC_SLICE_START (mbr, entry) = new_start;
3706   PC_SLICE_LENGTH (mbr, entry) = new_len;
3707 
3708   /* Make sure that the MBR has a valid signature.  */
3709   PC_MBR_SIG (mbr) = PC_MBR_SIGNATURE;
3710 
3711   /* Write back the MBR to the disk.  */
3712   buf_track = BUF_CACHE_INVALID;
3713   if (! rawwrite (current_drive, 0, mbr))
3714     return 1;
3715 
3716   return 0;
3717 }
3718 
3719 static struct builtin builtin_partnew =
3720 {
3721   "partnew",
3722   partnew_func,
3723   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
3724   "partnew PART TYPE START LEN",
3725   "Create a primary partition at the starting address START with the"
3726   " length LEN, with the type TYPE. START and LEN are in sector units."
3727 };
3728 
3729 
3730 /* parttype PART TYPE */
3731 static int
3732 parttype_func (char *arg, int flags)
3733 {
3734   int new_type;
3735   unsigned long part = 0xFFFFFF;
3736   unsigned long start, len, offset, ext_offset;
3737   int entry, type;
3738   char mbr[512];
3739 
3740   /* Get the drive and the partition.  */
3741   if (! set_device (arg))
3742     return 1;
3743 
3744   /* The drive must be a hard disk.  */
3745   if (! (current_drive & 0x80))
3746     {
3747       errnum = ERR_BAD_ARGUMENT;
3748       return 1;
3749     }
3750 
3751   /* The partition must be a PC slice.  */
3752   if ((current_partition >> 16) == 0xFF
3753       || (current_partition & 0xFFFF) != 0xFFFF)
3754     {
3755       errnum = ERR_BAD_ARGUMENT;
3756       return 1;
3757     }
3758 
3759   /* Get the new partition type.  */
3760   arg = skip_to (0, arg);
3761   if (! safe_parse_maxint (&arg, &new_type))
3762     return 1;
3763 
3764   /* The partition type is unsigned char.  */
3765   if (new_type > 0xFF)
3766     {
3767       errnum = ERR_BAD_ARGUMENT;
3768       return 1;
3769     }
3770 
3771   /* Look for the partition.  */
3772   while (next_partition (current_drive, 0xFFFFFF, &part, &type,
3773 			 &start, &len, &offset, &entry,
3774 			 &ext_offset, mbr))
3775     {
3776       if (part == current_partition)
3777 	{
3778 	  /* Found.  */
3779 
3780 	  /* Set the type to NEW_TYPE.  */
3781 	  PC_SLICE_TYPE (mbr, entry) = new_type;
3782 
3783 	  /* Write back the MBR to the disk.  */
3784 	  buf_track = BUF_CACHE_INVALID;
3785 	  if (! rawwrite (current_drive, offset, mbr))
3786 	    return 1;
3787 
3788 	  /* Succeed.  */
3789 	  return 0;
3790 	}
3791     }
3792 
3793   /* The partition was not found.  ERRNUM was set by next_partition.  */
3794   return 1;
3795 }
3796 
3797 static struct builtin builtin_parttype =
3798 {
3799   "parttype",
3800   parttype_func,
3801   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
3802   "parttype PART TYPE",
3803   "Change the type of the partition PART to TYPE."
3804 };
3805 
3806 
3807 /* password */
3808 static int
3809 password_func (char *arg, int flags)
3810 {
3811   int len;
3812   password_t type = PASSWORD_PLAIN;
3813 
3814 #ifdef USE_MD5_PASSWORDS
3815   if (grub_memcmp (arg, "--md5", 5) == 0)
3816     {
3817       type = PASSWORD_MD5;
3818       arg = skip_to (0, arg);
3819     }
3820 #endif
3821   if (grub_memcmp (arg, "--", 2) == 0)
3822     {
3823       type = PASSWORD_UNSUPPORTED;
3824       arg = skip_to (0, arg);
3825     }
3826 
3827   if ((flags & (BUILTIN_CMDLINE | BUILTIN_SCRIPT)) != 0)
3828     {
3829       /* Do password check! */
3830       char entered[32];
3831 
3832       /* Wipe out any previously entered password */
3833       entered[0] = 0;
3834       get_cmdline ("Password: ", entered, 31, '*', 0);
3835 
3836       nul_terminate (arg);
3837       if (check_password (entered, arg, type) != 0)
3838 	{
3839 	  errnum = ERR_PRIVILEGED;
3840 	  return 1;
3841 	}
3842     }
3843   else
3844     {
3845       len = grub_strlen (arg);
3846 
3847       /* PASSWORD NUL NUL ... */
3848       if (len + 2 > PASSWORD_BUFLEN)
3849 	{
3850 	  errnum = ERR_WONT_FIT;
3851 	  return 1;
3852 	}
3853 
3854       /* Copy the password and clear the rest of the buffer.  */
3855       password = (char *) PASSWORD_BUF;
3856       grub_memmove (password, arg, len);
3857       grub_memset (password + len, 0, PASSWORD_BUFLEN - len);
3858       password_type = type;
3859     }
3860   return 0;
3861 }
3862 
3863 static struct builtin builtin_password =
3864 {
3865   "password",
3866   password_func,
3867   BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_NO_ECHO,
3868   "password [--md5] PASSWD [FILE]",
3869   "If used in the first section of a menu file, disable all"
3870   " interactive editing control (menu entry editor and"
3871   " command line). If the password PASSWD is entered, it loads the"
3872   " FILE as a new config file and restarts the GRUB Stage 2. If you"
3873   " omit the argument FILE, then GRUB just unlocks privileged"
3874   " instructions.  You can also use it in the script section, in"
3875   " which case it will ask for the password, before continueing."
3876   " The option --md5 tells GRUB that PASSWD is encrypted with"
3877   " md5crypt."
3878 };
3879 
3880 
3881 /* pause */
3882 static int
3883 pause_func (char *arg, int flags)
3884 {
3885   printf("%s\n", arg);
3886 
3887   /* If ESC is returned, then abort this entry.  */
3888   if (ASCII_CHAR (getkey ()) == 27)
3889     return 1;
3890 
3891   return 0;
3892 }
3893 
3894 static struct builtin builtin_pause =
3895 {
3896   "pause",
3897   pause_func,
3898   BUILTIN_CMDLINE | BUILTIN_NO_ECHO,
3899   "pause [MESSAGE ...]",
3900   "Print MESSAGE, then wait until a key is pressed."
3901 };
3902 
3903 
3904 #ifdef GRUB_UTIL
3905 /* quit */
3906 static int
3907 quit_func (char *arg, int flags)
3908 {
3909   stop ();
3910 
3911   /* Never reach here.  */
3912   return 0;
3913 }
3914 
3915 static struct builtin builtin_quit =
3916 {
3917   "quit",
3918   quit_func,
3919   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3920   "quit",
3921   "Exit from the GRUB shell."
3922 };
3923 #endif /* GRUB_UTIL */
3924 
3925 
3926 #ifdef SUPPORT_NETBOOT
3927 /* rarp */
3928 static int
3929 rarp_func (char *arg, int flags)
3930 {
3931   if (! rarp ())
3932     {
3933       if (errnum == ERR_NONE)
3934 	errnum = ERR_DEV_VALUES;
3935 
3936       return 1;
3937     }
3938 
3939   /* Notify the configuration.  */
3940   print_network_configuration ();
3941   return 0;
3942 }
3943 
3944 static struct builtin builtin_rarp =
3945 {
3946   "rarp",
3947   rarp_func,
3948   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
3949   "rarp",
3950   "Initialize a network device via RARP."
3951 };
3952 #endif /* SUPPORT_NETBOOT */
3953 
3954 
3955 static int
3956 read_func (char *arg, int flags)
3957 {
3958   int addr;
3959 
3960   if (! safe_parse_maxint (&arg, &addr))
3961     return 1;
3962 
3963   grub_printf ("Address 0x%x: Value 0x%x\n",
3964 	       addr, *((unsigned *) RAW_ADDR (addr)));
3965   return 0;
3966 }
3967 
3968 static struct builtin builtin_read =
3969 {
3970   "read",
3971   read_func,
3972   BUILTIN_CMDLINE,
3973   "read ADDR",
3974   "Read a 32-bit value from memory at address ADDR and"
3975   " display it in hex format."
3976 };
3977 
3978 
3979 /* reboot */
3980 static int
3981 reboot_func (char *arg, int flags)
3982 {
3983   grub_reboot ();
3984 
3985   /* Never reach here.  */
3986   return 1;
3987 }
3988 
3989 static struct builtin builtin_reboot =
3990 {
3991   "reboot",
3992   reboot_func,
3993   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3994   "reboot",
3995   "Reboot your system."
3996 };
3997 
3998 
3999 /* Print the root device information.  */
4000 static void
4001 print_root_device (void)
4002 {
4003   if (saved_drive == NETWORK_DRIVE)
4004     {
4005       /* Network drive.  */
4006       grub_printf (" (nd):");
4007     }
4008   else if (saved_drive & 0x80)
4009     {
4010       /* Hard disk drive.  */
4011       grub_printf (" (hd%d", saved_drive - 0x80);
4012 
4013       if ((saved_partition & 0xFF0000) != 0xFF0000)
4014 	grub_printf (",%d", saved_partition >> 16);
4015 
4016       if ((saved_partition & 0x00FF00) != 0x00FF00)
4017 	grub_printf (",%c", ((saved_partition >> 8) & 0xFF) + 'a');
4018 
4019       grub_printf ("):");
4020     }
4021   else
4022     {
4023       /* Floppy disk drive.  */
4024       grub_printf (" (fd%d):", saved_drive);
4025     }
4026 
4027   /* Print the filesystem information.  */
4028   current_partition = saved_partition;
4029   current_drive = saved_drive;
4030   print_fsys_type ();
4031 }
4032 
4033 static int
4034 real_root_func (char *arg, int attempt_mount)
4035 {
4036   int hdbias = 0;
4037   char *biasptr;
4038   char *next;
4039 
4040   /* If ARG is empty, just print the current root device.  */
4041   if (! *arg)
4042     {
4043       print_root_device ();
4044       return 0;
4045     }
4046 
4047   /* Call set_device to get the drive and the partition in ARG.  */
4048   next = set_device (arg);
4049   if (! next)
4050     return 1;
4051 
4052   /* Ignore ERR_FSYS_MOUNT.  */
4053   if (attempt_mount)
4054     {
4055       if (! open_device () && errnum != ERR_FSYS_MOUNT)
4056 	return 1;
4057     }
4058   else
4059     {
4060       /* This is necessary, because the location of a partition table
4061 	 must be set appropriately.  */
4062       if (open_partition ())
4063 	{
4064 	  set_bootdev (0);
4065 	  if (errnum)
4066 	    return 1;
4067 	}
4068     }
4069 
4070   /* Clear ERRNUM.  */
4071   errnum = 0;
4072   saved_partition = current_partition;
4073   saved_drive = current_drive;
4074 
4075   if (attempt_mount)
4076     {
4077       /* BSD and chainloading evil hacks !!  */
4078       biasptr = skip_to (0, next);
4079       safe_parse_maxint (&biasptr, &hdbias);
4080       errnum = 0;
4081       bootdev = set_bootdev (hdbias);
4082       if (errnum)
4083 	return 1;
4084 
4085       /* Print the type of the filesystem.  */
4086       print_fsys_type ();
4087     }
4088 
4089   return 0;
4090 }
4091 
4092 static int
4093 root_func (char *arg, int flags)
4094 {
4095   is_zfs_mount = 0;
4096   return real_root_func (arg, 1);
4097 }
4098 
4099 static struct builtin builtin_root =
4100 {
4101   "root",
4102   root_func,
4103   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
4104   "root [DEVICE [HDBIAS]]",
4105   "Set the current \"root device\" to the device DEVICE, then"
4106   " attempt to mount it to get the partition size (for passing the"
4107   " partition descriptor in `ES:ESI', used by some chain-loaded"
4108   " bootloaders), the BSD drive-type (for booting BSD kernels using"
4109   " their native boot format), and correctly determine "
4110   " the PC partition where a BSD sub-partition is located. The"
4111   " optional HDBIAS parameter is a number to tell a BSD kernel"
4112   " how many BIOS drive numbers are on controllers before the current"
4113   " one. For example, if there is an IDE disk and a SCSI disk, and your"
4114   " FreeBSD root partition is on the SCSI disk, then use a `1' for HDBIAS."
4115 };
4116 
4117 
4118 /* findroot */
4119 int
4120 findroot_func (char *arg, int flags)
4121 {
4122   int ret;
4123   char root[32];
4124 
4125   if (grub_strlen(arg) >= BOOTSIGN_ARGLEN) {
4126   	errnum = ERR_BAD_ARGUMENT;
4127 	return 1;
4128   }
4129 
4130   if (arg[0] == '\0') {
4131   	errnum = ERR_BAD_ARGUMENT;
4132 	return 1;
4133   }
4134 
4135   find_best_root = 1;
4136   best_drive = 0;
4137   best_part = 0;
4138   ret = find_common(arg, root, 1, flags);
4139   if (ret != 0)
4140 	return (ret);
4141   find_best_root = 0;
4142 
4143   return real_root_func (root, 1);
4144 }
4145 
4146 static struct builtin builtin_findroot =
4147 {
4148   "findroot",
4149   findroot_func,
4150   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
4151   "findroot  <SIGNATURE | (SIGNATURE,partition[,slice])>",
4152   "Searches across all partitions for the file name SIGNATURE."
4153   " GRUB looks only in the directory /boot/grub/bootsign for the"
4154   " filename and it stops as soon as it finds the first instance of"
4155   " the file - so to be useful the name of the signature file must be"
4156   " unique across all partitions. Once the signature file is found,"
4157   " GRUB invokes the \"root\" command on that partition."
4158   " An optional partition and slice may be specified to optimize the search."
4159 };
4160 
4161 
4162 /*
4163  * COMMAND to override the default root filesystem for ZFS
4164  *	bootfs pool/fs
4165  */
4166 static int
4167 bootfs_func (char *arg, int flags)
4168 {
4169 	int hdbias = 0;
4170 	char *biasptr;
4171 	char *next;
4172 
4173 	if (! *arg) {
4174 	    if (current_bootfs[0] != '\0')
4175 		grub_printf ("The zfs boot filesystem is set to '%s'.\n",
4176 				current_bootfs);
4177 	    else if (current_rootpool[0] != 0 && current_bootfs_obj != 0)
4178 		grub_printf("The zfs boot filesystem is <default: %s/%u>.",
4179 				current_rootpool, current_bootfs_obj);
4180 	    else
4181 		grub_printf ("The zfs boot filesystem will be derived from "
4182 			"the default bootfs pool property.\n");
4183 
4184 	    return (1);
4185 	}
4186 
4187 	/* Verify the zfs filesystem name */
4188 	if (arg[0] == '/' || arg[0] == '\0') {
4189 		errnum = ERR_BAD_ARGUMENT;
4190 		return 0;
4191 	}
4192 	if (current_rootpool[0] != 0 && grub_strncmp(arg,
4193 	    current_rootpool, strlen(current_rootpool))) {
4194 		errnum = ERR_BAD_ARGUMENT;
4195 		return 0;
4196 	}
4197 
4198 	grub_memmove(current_bootfs, arg, MAXNAMELEN);
4199 
4200 	return (1);
4201 }
4202 
4203 static struct builtin builtin_bootfs =
4204 {
4205   "bootfs",
4206   bootfs_func,
4207   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
4208   "bootfs [ZFSBOOTFS]",
4209   "Set the current zfs boot filesystem to ZFSBOOTFS (rootpool/rootfs)."
4210 };
4211 
4212 
4213 /* rootnoverify */
4214 static int
4215 rootnoverify_func (char *arg, int flags)
4216 {
4217   return real_root_func (arg, 0);
4218 }
4219 
4220 static struct builtin builtin_rootnoverify =
4221 {
4222   "rootnoverify",
4223   rootnoverify_func,
4224   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
4225   "rootnoverify [DEVICE [HDBIAS]]",
4226   "Similar to `root', but don't attempt to mount the partition. This"
4227   " is useful for when an OS is outside of the area of the disk that"
4228   " GRUB can read, but setting the correct root device is still"
4229   " desired. Note that the items mentioned in `root' which"
4230   " derived from attempting the mount will NOT work correctly."
4231 };
4232 
4233 
4234 /* savedefault */
4235 static int
4236 savedefault_func (char *arg, int flags)
4237 {
4238 #if !defined(SUPPORT_DISKLESS) && !defined(GRUB_UTIL)
4239   unsigned long tmp_drive = saved_drive;
4240   unsigned long tmp_partition = saved_partition;
4241   char *default_file = (char *) DEFAULT_FILE_BUF;
4242   char buf[10];
4243   char sect[SECTOR_SIZE];
4244   int entryno;
4245   int sector_count = 0;
4246   unsigned int saved_sectors[2];
4247   int saved_offsets[2];
4248   int saved_lengths[2];
4249 
4250   /* not supported for zfs root */
4251   if (is_zfs_mount == 1) {
4252 	return (0); /* no-op */
4253   }
4254 
4255   /* Save sector information about at most two sectors.  */
4256   auto void disk_read_savesect_func (unsigned int sector, int offset,
4257       int length);
4258   void disk_read_savesect_func (unsigned int sector, int offset, int length)
4259     {
4260       if (sector_count < 2)
4261 	{
4262 	  saved_sectors[sector_count] = sector;
4263 	  saved_offsets[sector_count] = offset;
4264 	  saved_lengths[sector_count] = length;
4265 	}
4266       sector_count++;
4267     }
4268 
4269   /* This command is only useful when you boot an entry from the menu
4270      interface.  */
4271   if (! (flags & BUILTIN_SCRIPT))
4272     {
4273       errnum = ERR_UNRECOGNIZED;
4274       return 1;
4275     }
4276 
4277   /* Determine a saved entry number.  */
4278   if (*arg)
4279     {
4280       if (grub_memcmp (arg, "fallback", sizeof ("fallback") - 1) == 0)
4281 	{
4282 	  int i;
4283 	  int index = 0;
4284 
4285 	  for (i = 0; i < MAX_FALLBACK_ENTRIES; i++)
4286 	    {
4287 	      if (fallback_entries[i] < 0)
4288 		break;
4289 	      if (fallback_entries[i] == current_entryno)
4290 		{
4291 		  index = i + 1;
4292 		  break;
4293 		}
4294 	    }
4295 
4296 	  if (index >= MAX_FALLBACK_ENTRIES || fallback_entries[index] < 0)
4297 	    {
4298 	      /* This is the last.  */
4299 	      errnum = ERR_BAD_ARGUMENT;
4300 	      return 1;
4301 	    }
4302 
4303 	  entryno = fallback_entries[index];
4304 	}
4305       else if (! safe_parse_maxint (&arg, &entryno))
4306 	return 1;
4307     }
4308   else
4309     entryno = current_entryno;
4310 
4311   /* Open the default file.  */
4312   saved_drive = boot_drive;
4313   saved_partition = install_partition;
4314   if (grub_open (default_file))
4315     {
4316       int len;
4317 
4318       disk_read_hook = disk_read_savesect_func;
4319       len = grub_read (buf, sizeof (buf));
4320       disk_read_hook = 0;
4321       grub_close ();
4322 
4323       if (len != sizeof (buf))
4324 	{
4325 	  /* This is too small. Do not modify the file manually, please!  */
4326 	  errnum = ERR_READ;
4327 	  goto fail;
4328 	}
4329 
4330       if (sector_count > 2)
4331 	{
4332 	  /* Is this possible?! Too fragmented!  */
4333 	  errnum = ERR_FSYS_CORRUPT;
4334 	  goto fail;
4335 	}
4336 
4337       /* Set up a string to be written.  */
4338       grub_memset (buf, '\n', sizeof (buf));
4339       grub_sprintf (buf, "%d", entryno);
4340 
4341       if (saved_lengths[0] < sizeof (buf))
4342 	{
4343 	  /* The file is anchored to another file and the first few bytes
4344 	     are spanned in two sectors. Uggh...  */
4345 	  if (! rawread (current_drive, saved_sectors[0], 0, SECTOR_SIZE,
4346 			 sect))
4347 	    goto fail;
4348 	  grub_memmove (sect + saved_offsets[0], buf, saved_lengths[0]);
4349 	  if (! rawwrite (current_drive, saved_sectors[0], sect))
4350 	    goto fail;
4351 
4352 	  if (! rawread (current_drive, saved_sectors[1], 0, SECTOR_SIZE,
4353 			 sect))
4354 	    goto fail;
4355 	  grub_memmove (sect + saved_offsets[1],
4356 			buf + saved_lengths[0],
4357 			sizeof (buf) - saved_lengths[0]);
4358 	  if (! rawwrite (current_drive, saved_sectors[1], sect))
4359 	    goto fail;
4360 	}
4361       else
4362 	{
4363 	  /* This is a simple case. It fits into a single sector.  */
4364 	  if (! rawread (current_drive, saved_sectors[0], 0, SECTOR_SIZE,
4365 			 sect))
4366 	    goto fail;
4367 	  grub_memmove (sect + saved_offsets[0], buf, sizeof (buf));
4368 	  if (! rawwrite (current_drive, saved_sectors[0], sect))
4369 	    goto fail;
4370 	}
4371 
4372       /* Clear the cache.  */
4373       buf_track = BUF_CACHE_INVALID;
4374     }
4375 
4376  fail:
4377   saved_drive = tmp_drive;
4378   saved_partition = tmp_partition;
4379   return errnum;
4380 #else /* ! SUPPORT_DISKLESS && ! GRUB_UTIL */
4381   errnum = ERR_UNRECOGNIZED;
4382   return 1;
4383 #endif /* ! SUPPORT_DISKLESS && ! GRUB_UTIL */
4384 }
4385 
4386 static struct builtin builtin_savedefault =
4387 {
4388   "savedefault",
4389   savedefault_func,
4390   BUILTIN_CMDLINE,
4391   "savedefault [NUM | `fallback']",
4392   "Save the current entry as the default boot entry if no argument is"
4393   " specified. If a number is specified, this number is saved. If"
4394   " `fallback' is used, next fallback entry is saved."
4395 };
4396 
4397 
4398 #ifdef SUPPORT_SERIAL
4399 /* serial */
4400 static int
4401 serial_func (char *arg, int flags)
4402 {
4403   unsigned short port = serial_hw_get_port (0);
4404   unsigned int speed = 9600;
4405   int word_len = UART_8BITS_WORD;
4406   int parity = UART_NO_PARITY;
4407   int stop_bit_len = UART_1_STOP_BIT;
4408 
4409   /* Process GNU-style long options.
4410      FIXME: We should implement a getopt-like function, to avoid
4411      duplications.  */
4412   while (1)
4413     {
4414       if (grub_memcmp (arg, "--unit=", sizeof ("--unit=") - 1) == 0)
4415 	{
4416 	  char *p = arg + sizeof ("--unit=") - 1;
4417 	  int unit;
4418 
4419 	  if (! safe_parse_maxint (&p, &unit))
4420 	    return 1;
4421 
4422 	  if (unit < 0 || unit > 3)
4423 	    {
4424 	      errnum = ERR_DEV_VALUES;
4425 	      return 1;
4426 	    }
4427 
4428 	  port = serial_hw_get_port (unit);
4429 	}
4430       else if (grub_memcmp (arg, "--speed=", sizeof ("--speed=") - 1) == 0)
4431 	{
4432 	  char *p = arg + sizeof ("--speed=") - 1;
4433 	  int num;
4434 
4435 	  if (! safe_parse_maxint (&p, &num))
4436 	    return 1;
4437 
4438 	  speed = (unsigned int) num;
4439 	}
4440       else if (grub_memcmp (arg, "--port=", sizeof ("--port=") - 1) == 0)
4441 	{
4442 	  char *p = arg + sizeof ("--port=") - 1;
4443 	  int num;
4444 
4445 	  if (! safe_parse_maxint (&p, &num))
4446 	    return 1;
4447 
4448 	  port = (unsigned short) num;
4449 	}
4450       else if (grub_memcmp (arg, "--word=", sizeof ("--word=") - 1) == 0)
4451 	{
4452 	  char *p = arg + sizeof ("--word=") - 1;
4453 	  int len;
4454 
4455 	  if (! safe_parse_maxint (&p, &len))
4456 	    return 1;
4457 
4458 	  switch (len)
4459 	    {
4460 	    case 5: word_len = UART_5BITS_WORD; break;
4461 	    case 6: word_len = UART_6BITS_WORD; break;
4462 	    case 7: word_len = UART_7BITS_WORD; break;
4463 	    case 8: word_len = UART_8BITS_WORD; break;
4464 	    default:
4465 	      errnum = ERR_BAD_ARGUMENT;
4466 	      return 1;
4467 	    }
4468 	}
4469       else if (grub_memcmp (arg, "--stop=", sizeof ("--stop=") - 1) == 0)
4470 	{
4471 	  char *p = arg + sizeof ("--stop=") - 1;
4472 	  int len;
4473 
4474 	  if (! safe_parse_maxint (&p, &len))
4475 	    return 1;
4476 
4477 	  switch (len)
4478 	    {
4479 	    case 1: stop_bit_len = UART_1_STOP_BIT; break;
4480 	    case 2: stop_bit_len = UART_2_STOP_BITS; break;
4481 	    default:
4482 	      errnum = ERR_BAD_ARGUMENT;
4483 	      return 1;
4484 	    }
4485 	}
4486       else if (grub_memcmp (arg, "--parity=", sizeof ("--parity=") - 1) == 0)
4487 	{
4488 	  char *p = arg + sizeof ("--parity=") - 1;
4489 
4490 	  if (grub_memcmp (p, "no", sizeof ("no") - 1) == 0)
4491 	    parity = UART_NO_PARITY;
4492 	  else if (grub_memcmp (p, "odd", sizeof ("odd") - 1) == 0)
4493 	    parity = UART_ODD_PARITY;
4494 	  else if (grub_memcmp (p, "even", sizeof ("even") - 1) == 0)
4495 	    parity = UART_EVEN_PARITY;
4496 	  else
4497 	    {
4498 	      errnum = ERR_BAD_ARGUMENT;
4499 	      return 1;
4500 	    }
4501 	}
4502 # ifdef GRUB_UTIL
4503       /* In the grub shell, don't use any port number but open a tty
4504 	 device instead.  */
4505       else if (grub_memcmp (arg, "--device=", sizeof ("--device=") - 1) == 0)
4506 	{
4507 	  char *p = arg + sizeof ("--device=") - 1;
4508 	  char dev[256];	/* XXX */
4509 	  char *q = dev;
4510 
4511 	  while (*p && ! grub_isspace (*p))
4512 	    *q++ = *p++;
4513 
4514 	  *q = 0;
4515 	  serial_set_device (dev);
4516 	}
4517 # endif /* GRUB_UTIL */
4518       else
4519 	break;
4520 
4521       arg = skip_to (0, arg);
4522     }
4523 
4524   /* Initialize the serial unit.  */
4525   if (! serial_hw_init (port, speed, word_len, parity, stop_bit_len))
4526     {
4527       errnum = ERR_BAD_ARGUMENT;
4528       return 1;
4529     }
4530 
4531   return 0;
4532 }
4533 
4534 static struct builtin builtin_serial =
4535 {
4536   "serial",
4537   serial_func,
4538   BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
4539   "serial [--unit=UNIT] [--port=PORT] [--speed=SPEED] [--word=WORD] [--parity=PARITY] [--stop=STOP] [--device=DEV]",
4540   "Initialize a serial device. UNIT is a digit that specifies which serial"
4541   " device is used (e.g. 0 == COM1). If you need to specify the port number,"
4542   " set it by --port. SPEED is the DTE-DTE speed. WORD is the word length,"
4543   " PARITY is the type of parity, which is one of `no', `odd' and `even'."
4544   " STOP is the length of stop bit(s). The option --device can be used only"
4545   " in the grub shell, which specifies the file name of a tty device. The"
4546   " default values are COM1, 9600, 8N1."
4547 };
4548 #endif /* SUPPORT_SERIAL */
4549 
4550 
4551 /* setkey */
4552 struct keysym
4553 {
4554   char *unshifted_name;			/* the name in unshifted state */
4555   char *shifted_name;			/* the name in shifted state */
4556   unsigned char unshifted_ascii;	/* the ascii code in unshifted state */
4557   unsigned char shifted_ascii;		/* the ascii code in shifted state */
4558   unsigned char keycode;		/* keyboard scancode */
4559 };
4560 
4561 /* The table for key symbols. If the "shifted" member of an entry is
4562    NULL, the entry does not have shifted state.  */
4563 static struct keysym keysym_table[] =
4564 {
4565   {"escape",		0,		0x1b,	0,	0x01},
4566   {"1",			"exclam",	'1',	'!',	0x02},
4567   {"2",			"at",		'2',	'@',	0x03},
4568   {"3",			"numbersign",	'3',	'#',	0x04},
4569   {"4",			"dollar",	'4',	'$',	0x05},
4570   {"5",			"percent",	'5',	'%',	0x06},
4571   {"6",			"caret",	'6',	'^',	0x07},
4572   {"7",			"ampersand",	'7',	'&',	0x08},
4573   {"8",			"asterisk",	'8',	'*',	0x09},
4574   {"9",			"parenleft",	'9',	'(',	0x0a},
4575   {"0",			"parenright",	'0',	')',	0x0b},
4576   {"minus",		"underscore",	'-',	'_',	0x0c},
4577   {"equal",		"plus",		'=',	'+',	0x0d},
4578   {"backspace",		0,		'\b',	0,	0x0e},
4579   {"tab",		0,		'\t',	0,	0x0f},
4580   {"q",			"Q",		'q',	'Q',	0x10},
4581   {"w",			"W",		'w',	'W',	0x11},
4582   {"e",			"E",		'e',	'E',	0x12},
4583   {"r",			"R",		'r',	'R',	0x13},
4584   {"t",			"T",		't',	'T',	0x14},
4585   {"y",			"Y",		'y',	'Y',	0x15},
4586   {"u",			"U",		'u',	'U',	0x16},
4587   {"i",			"I",		'i',	'I',	0x17},
4588   {"o",			"O",		'o',	'O',	0x18},
4589   {"p",			"P",		'p',	'P',	0x19},
4590   {"bracketleft",	"braceleft",	'[',	'{',	0x1a},
4591   {"bracketright",	"braceright",	']',	'}',	0x1b},
4592   {"enter",		0,		'\n',	0,	0x1c},
4593   {"control",		0,		0,	0,	0x1d},
4594   {"a",			"A",		'a',	'A',	0x1e},
4595   {"s",			"S",		's',	'S',	0x1f},
4596   {"d",			"D",		'd',	'D',	0x20},
4597   {"f",			"F",		'f',	'F',	0x21},
4598   {"g",			"G",		'g',	'G',	0x22},
4599   {"h",			"H",		'h',	'H',	0x23},
4600   {"j",			"J",		'j',	'J',	0x24},
4601   {"k",			"K",		'k',	'K',	0x25},
4602   {"l",			"L",		'l',	'L',	0x26},
4603   {"semicolon",		"colon",	';',	':',	0x27},
4604   {"quote",		"doublequote",	'\'',	'"',	0x28},
4605   {"backquote",		"tilde",	'`',	'~',	0x29},
4606   {"shift",		0,		0,	0,	0x2a},
4607   {"backslash",		"bar",		'\\',	'|',	0x2b},
4608   {"z",			"Z",		'z',	'Z',	0x2c},
4609   {"x",			"X",		'x',	'X',	0x2d},
4610   {"c",			"C",		'c',	'C',	0x2e},
4611   {"v",			"V",		'v',	'V',	0x2f},
4612   {"b",			"B",		'b',	'B',	0x30},
4613   {"n",			"N",		'n',	'N',	0x31},
4614   {"m",			"M",		'm',	'M',	0x32},
4615   {"comma",		"less",		',',	'<',	0x33},
4616   {"period",		"greater",	'.',	'>',	0x34},
4617   {"slash",		"question",	'/',	'?',	0x35},
4618   {"alt",		0,		0,	0,	0x38},
4619   {"space",		0,		' ',	0,	0x39},
4620   {"capslock",		0,		0,	0,	0x3a},
4621   {"F1",		0,		0,	0,	0x3b},
4622   {"F2",		0,		0,	0,	0x3c},
4623   {"F3",		0,		0,	0,	0x3d},
4624   {"F4",		0,		0,	0,	0x3e},
4625   {"F5",		0,		0,	0,	0x3f},
4626   {"F6",		0,		0,	0,	0x40},
4627   {"F7",		0,		0,	0,	0x41},
4628   {"F8",		0,		0,	0,	0x42},
4629   {"F9",		0,		0,	0,	0x43},
4630   {"F10",		0,		0,	0,	0x44},
4631   /* Caution: do not add NumLock here! we cannot deal with it properly.  */
4632   {"delete",		0,		0x7f,	0,	0x53}
4633 };
4634 
4635 static int
4636 setkey_func (char *arg, int flags)
4637 {
4638   char *to_key, *from_key;
4639   int to_code, from_code;
4640   int map_in_interrupt = 0;
4641 
4642   auto int find_key_code (char *key);
4643   auto int find_ascii_code (char *key);
4644 
4645   auto int find_key_code (char *key)
4646     {
4647       int i;
4648 
4649       for (i = 0; i < sizeof (keysym_table) / sizeof (keysym_table[0]); i++)
4650 	{
4651 	  if (keysym_table[i].unshifted_name &&
4652 	      grub_strcmp (key, keysym_table[i].unshifted_name) == 0)
4653 	    return keysym_table[i].keycode;
4654 	  else if (keysym_table[i].shifted_name &&
4655 		   grub_strcmp (key, keysym_table[i].shifted_name) == 0)
4656 	    return keysym_table[i].keycode;
4657 	}
4658 
4659       return 0;
4660     }
4661 
4662   auto int find_ascii_code (char *key)
4663     {
4664       int i;
4665 
4666       for (i = 0; i < sizeof (keysym_table) / sizeof (keysym_table[0]); i++)
4667 	{
4668 	  if (keysym_table[i].unshifted_name &&
4669 	      grub_strcmp (key, keysym_table[i].unshifted_name) == 0)
4670 	    return keysym_table[i].unshifted_ascii;
4671 	  else if (keysym_table[i].shifted_name &&
4672 		   grub_strcmp (key, keysym_table[i].shifted_name) == 0)
4673 	    return keysym_table[i].shifted_ascii;
4674 	}
4675 
4676       return 0;
4677     }
4678 
4679   to_key = arg;
4680   from_key = skip_to (0, to_key);
4681 
4682   if (! *to_key)
4683     {
4684       /* If the user specifies no argument, reset the key mappings.  */
4685       grub_memset (bios_key_map, 0, KEY_MAP_SIZE * sizeof (unsigned short));
4686       grub_memset (ascii_key_map, 0, KEY_MAP_SIZE * sizeof (unsigned short));
4687 
4688       return 0;
4689     }
4690   else if (! *from_key)
4691     {
4692       /* The user must specify two arguments or zero argument.  */
4693       errnum = ERR_BAD_ARGUMENT;
4694       return 1;
4695     }
4696 
4697   nul_terminate (to_key);
4698   nul_terminate (from_key);
4699 
4700   to_code = find_ascii_code (to_key);
4701   from_code = find_ascii_code (from_key);
4702   if (! to_code || ! from_code)
4703     {
4704       map_in_interrupt = 1;
4705       to_code = find_key_code (to_key);
4706       from_code = find_key_code (from_key);
4707       if (! to_code || ! from_code)
4708 	{
4709 	  errnum = ERR_BAD_ARGUMENT;
4710 	  return 1;
4711 	}
4712     }
4713 
4714   if (map_in_interrupt)
4715     {
4716       int i;
4717 
4718       /* Find an empty slot.  */
4719       for (i = 0; i < KEY_MAP_SIZE; i++)
4720 	{
4721 	  if ((bios_key_map[i] & 0xff) == from_code)
4722 	    /* Perhaps the user wants to overwrite the map.  */
4723 	    break;
4724 
4725 	  if (! bios_key_map[i])
4726 	    break;
4727 	}
4728 
4729       if (i == KEY_MAP_SIZE)
4730 	{
4731 	  errnum = ERR_WONT_FIT;
4732 	  return 1;
4733 	}
4734 
4735       if (to_code == from_code)
4736 	/* If TO is equal to FROM, delete the entry.  */
4737 	grub_memmove ((char *) &bios_key_map[i],
4738 		      (char *) &bios_key_map[i + 1],
4739 		      sizeof (unsigned short) * (KEY_MAP_SIZE - i));
4740       else
4741 	bios_key_map[i] = (to_code << 8) | from_code;
4742 
4743       /* Ugly but should work.  */
4744       unset_int15_handler ();
4745       set_int15_handler ();
4746     }
4747   else
4748     {
4749       int i;
4750 
4751       /* Find an empty slot.  */
4752       for (i = 0; i < KEY_MAP_SIZE; i++)
4753 	{
4754 	  if ((ascii_key_map[i] & 0xff) == from_code)
4755 	    /* Perhaps the user wants to overwrite the map.  */
4756 	    break;
4757 
4758 	  if (! ascii_key_map[i])
4759 	    break;
4760 	}
4761 
4762       if (i == KEY_MAP_SIZE)
4763 	{
4764 	  errnum = ERR_WONT_FIT;
4765 	  return 1;
4766 	}
4767 
4768       if (to_code == from_code)
4769 	/* If TO is equal to FROM, delete the entry.  */
4770 	grub_memmove ((char *) &ascii_key_map[i],
4771 		      (char *) &ascii_key_map[i + 1],
4772 		      sizeof (unsigned short) * (KEY_MAP_SIZE - i));
4773       else
4774 	ascii_key_map[i] = (to_code << 8) | from_code;
4775     }
4776 
4777   return 0;
4778 }
4779 
4780 static struct builtin builtin_setkey =
4781 {
4782   "setkey",
4783   setkey_func,
4784   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
4785   "setkey [TO_KEY FROM_KEY]",
4786   "Change the keyboard map. The key FROM_KEY is mapped to the key TO_KEY."
4787   " A key must be an alphabet, a digit, or one of these: escape, exclam,"
4788   " at, numbersign, dollar, percent, caret, ampersand, asterisk, parenleft,"
4789   " parenright, minus, underscore, equal, plus, backspace, tab, bracketleft,"
4790   " braceleft, bracketright, braceright, enter, control, semicolon, colon,"
4791   " quote, doublequote, backquote, tilde, shift, backslash, bar, comma,"
4792   " less, period, greater, slash, question, alt, space, capslock, FX (X"
4793   " is a digit), and delete. If no argument is specified, reset key"
4794   " mappings."
4795 };
4796 
4797 
4798 /* setup */
4799 static int
4800 setup_func (char *arg, int flags)
4801 {
4802   /* Point to the string of the installed drive/partition.  */
4803   char *install_ptr;
4804   /* Point to the string of the drive/parition where the GRUB images
4805      reside.  */
4806   char *image_ptr;
4807   unsigned long installed_drive, installed_partition;
4808   unsigned long image_drive, image_partition;
4809   unsigned long tmp_drive, tmp_partition;
4810   char stage1[64];
4811   char stage2[64];
4812   char config_filename[64];
4813   char real_config_filename[64];
4814   char cmd_arg[256];
4815   char device[16];
4816   char *buffer = (char *) RAW_ADDR (0x100000);
4817   int is_force_lba = 0;
4818   char *stage2_arg = 0;
4819   char *prefix = 0;
4820 
4821   auto int check_file (char *file);
4822   auto void sprint_device (int drive, int partition);
4823   auto int embed_stage1_5 (char * stage1_5, int drive, int partition);
4824 
4825   /* Check if the file FILE exists like Autoconf.  */
4826   int check_file (char *file)
4827     {
4828       int ret;
4829 
4830       grub_printf (" Checking if \"%s\" exists... ", file);
4831       ret = grub_open (file);
4832       if (ret)
4833 	{
4834 	  grub_close ();
4835 	  grub_printf ("yes\n");
4836 	}
4837       else
4838 	grub_printf ("no\n");
4839 
4840       return ret;
4841     }
4842 
4843   /* Construct a device name in DEVICE.  */
4844   void sprint_device (int drive, int partition)
4845     {
4846       grub_sprintf (device, "(%cd%d",
4847 		    (drive & 0x80) ? 'h' : 'f',
4848 		    drive & ~0x80);
4849       if ((partition & 0xFF0000) != 0xFF0000)
4850 	{
4851 	  char tmp[16];
4852 	  grub_sprintf (tmp, ",%d", (partition >> 16) & 0xFF);
4853 	  grub_strncat (device, tmp, 256);
4854 	}
4855       if ((partition & 0x00FF00) != 0x00FF00)
4856 	{
4857 	  char tmp[16];
4858 	  grub_sprintf (tmp, ",%c", 'a' + ((partition >> 8) & 0xFF));
4859 	  grub_strncat (device, tmp, 256);
4860 	}
4861       grub_strncat (device, ")", 256);
4862     }
4863 
4864   int embed_stage1_5 (char *stage1_5, int drive, int partition)
4865     {
4866       /* We install GRUB into the MBR, so try to embed the
4867 	 Stage 1.5 in the sectors right after the MBR.  */
4868       sprint_device (drive, partition);
4869       grub_sprintf (cmd_arg, "%s %s", stage1_5, device);
4870 
4871       /* Notify what will be run.  */
4872       grub_printf (" Running \"embed %s\"... ", cmd_arg);
4873 
4874       embed_func (cmd_arg, flags);
4875       if (! errnum)
4876 	{
4877 	  /* Construct the blocklist representation.  */
4878 	  grub_sprintf (buffer, "%s%s", device, embed_info);
4879 	  grub_printf ("succeeded\n");
4880 	  return 1;
4881 	}
4882       else
4883 	{
4884 	  grub_printf ("failed (this is not fatal)\n");
4885 	  return 0;
4886 	}
4887     }
4888 
4889   struct stage1_5_map {
4890     char *fsys;
4891     char *name;
4892   };
4893   struct stage1_5_map stage1_5_map[] =
4894   {
4895     {"ext2fs",   "/e2fs_stage1_5"},
4896     {"fat",      "/fat_stage1_5"},
4897     {"ufs2",     "/ufs2_stage1_5"},
4898     {"ffs",      "/ffs_stage1_5"},
4899     {"iso9660",  "/iso9660_stage1_5"},
4900     {"jfs",      "/jfs_stage1_5"},
4901     {"minix",    "/minix_stage1_5"},
4902     {"reiserfs", "/reiserfs_stage1_5"},
4903     {"vstafs",   "/vstafs_stage1_5"},
4904     {"xfs",      "/xfs_stage1_5"},
4905     {"ufs",      "/ufs_stage1_5"}
4906   };
4907 
4908   tmp_drive = saved_drive;
4909   tmp_partition = saved_partition;
4910 
4911   /* Check if the user specifies --force-lba.  */
4912   while (1)
4913     {
4914       if (grub_memcmp ("--force-lba", arg, sizeof ("--force-lba") - 1) == 0)
4915 	{
4916 	  is_force_lba = 1;
4917 	  arg = skip_to (0, arg);
4918 	}
4919       else if (grub_memcmp ("--prefix=", arg, sizeof ("--prefix=") - 1) == 0)
4920 	{
4921 	  prefix = arg + sizeof ("--prefix=") - 1;
4922 	  arg = skip_to (0, arg);
4923 	  nul_terminate (prefix);
4924 	}
4925 #ifdef GRUB_UTIL
4926       else if (grub_memcmp ("--stage2=", arg, sizeof ("--stage2=") - 1) == 0)
4927 	{
4928 	  stage2_arg = arg;
4929 	  arg = skip_to (0, arg);
4930 	  nul_terminate (stage2_arg);
4931 	}
4932 #endif /* GRUB_UTIL */
4933       else
4934 	break;
4935     }
4936 
4937   install_ptr = arg;
4938   image_ptr = skip_to (0, install_ptr);
4939 
4940   /* Make sure that INSTALL_PTR is valid.  */
4941   set_device (install_ptr);
4942   if (errnum)
4943     return 1;
4944 
4945   installed_drive = current_drive;
4946   installed_partition = current_partition;
4947 
4948   /* Mount the drive pointed by IMAGE_PTR.  */
4949   if (*image_ptr)
4950     {
4951       /* If the drive/partition where the images reside is specified,
4952 	 get the drive and the partition.  */
4953       set_device (image_ptr);
4954       if (errnum)
4955 	return 1;
4956     }
4957   else
4958     {
4959       /* If omitted, use SAVED_PARTITION and SAVED_DRIVE.  */
4960       current_drive = saved_drive;
4961       current_partition = saved_partition;
4962     }
4963 
4964   image_drive = saved_drive = current_drive;
4965   image_partition = saved_partition = current_partition;
4966 
4967   /* Open it.  */
4968   if (! open_device ())
4969     goto fail;
4970 
4971   /* Check if stage1 exists. If the user doesn't specify the option
4972      `--prefix', attempt /boot/grub and /grub.  */
4973   /* NOTE: It is dangerous to run this command without `--prefix' in the
4974      grub shell, since that affects `--stage2'.  */
4975   if (! prefix)
4976     {
4977       prefix = "/boot/grub";
4978       grub_sprintf (stage1, "%s%s", prefix, "/stage1");
4979       if (! check_file (stage1))
4980 	{
4981 	  errnum = ERR_NONE;
4982 	  prefix = "/grub";
4983 	  grub_sprintf (stage1, "%s%s", prefix, "/stage1");
4984 	  if (! check_file (stage1))
4985 	    goto fail;
4986 	}
4987     }
4988   else
4989     {
4990       grub_sprintf (stage1, "%s%s", prefix, "/stage1");
4991       if (! check_file (stage1))
4992 	goto fail;
4993     }
4994 
4995   /* The prefix was determined.  */
4996   grub_sprintf (stage2, "%s%s", prefix, "/stage2");
4997   grub_sprintf (config_filename, "%s%s", prefix, "/menu.lst");
4998   *real_config_filename = 0;
4999 
5000   /* Check if stage2 exists.  */
5001   if (! check_file (stage2))
5002     goto fail;
5003 
5004   {
5005     char *fsys = fsys_table[fsys_type].name;
5006     int i;
5007     int size = sizeof (stage1_5_map) / sizeof (stage1_5_map[0]);
5008 
5009     /* Iterate finding the same filesystem name as FSYS.  */
5010     for (i = 0; i < size; i++)
5011       if (grub_strcmp (fsys, stage1_5_map[i].fsys) == 0)
5012 	{
5013 	  /* OK, check if the Stage 1.5 exists.  */
5014 	  char stage1_5[64];
5015 
5016 	  grub_sprintf (stage1_5, "%s%s", prefix, stage1_5_map[i].name);
5017 	  if (check_file (stage1_5))
5018 	    {
5019 	      if (embed_stage1_5 (stage1_5,
5020 				    installed_drive, installed_partition)
5021 		  || embed_stage1_5 (stage1_5,
5022 				     image_drive, image_partition))
5023 		{
5024 		  grub_strcpy (real_config_filename, config_filename);
5025 		  sprint_device (image_drive, image_partition);
5026 		  grub_sprintf (config_filename, "%s%s", device, stage2);
5027 		  grub_strcpy (stage2, buffer);
5028 		}
5029 	    }
5030 	  errnum = 0;
5031 	  break;
5032 	}
5033   }
5034 
5035   /* Construct a string that is used by the command "install" as its
5036      arguments.  */
5037   sprint_device (installed_drive, installed_partition);
5038 
5039 #if 1
5040   /* Don't embed a drive number unnecessarily.  */
5041   grub_sprintf (cmd_arg, "%s%s%s%s %s%s %s p %s %s",
5042 		is_force_lba? "--force-lba " : "",
5043 		stage2_arg? stage2_arg : "",
5044 		stage2_arg? " " : "",
5045 		stage1,
5046 		(installed_drive != image_drive) ? "d " : "",
5047 		device,
5048 		stage2,
5049 		config_filename,
5050 		real_config_filename);
5051 #else /* NOT USED */
5052   /* This code was used, because we belived some BIOSes had a problem
5053      that they didn't pass a booting drive correctly. It turned out,
5054      however, stage1 could trash a booting drive when checking LBA support,
5055      because some BIOSes modified the register %dx in INT 13H, AH=48H.
5056      So it becamed unclear whether GRUB should use a pre-defined booting
5057      drive or not. If the problem still exists, it would be necessary to
5058      switch back to this code.  */
5059   grub_sprintf (cmd_arg, "%s%s%s%s d %s %s p %s %s",
5060 		is_force_lba? "--force-lba " : "",
5061 		stage2_arg? stage2_arg : "",
5062 		stage2_arg? " " : "",
5063 		stage1,
5064 		device,
5065 		stage2,
5066 		config_filename,
5067 		real_config_filename);
5068 #endif /* NOT USED */
5069 
5070   /* Notify what will be run.  */
5071   grub_printf (" Running \"install %s\"... ", cmd_arg);
5072 
5073   /* Make sure that SAVED_DRIVE and SAVED_PARTITION are identical
5074      with IMAGE_DRIVE and IMAGE_PARTITION, respectively.  */
5075   saved_drive = image_drive;
5076   saved_partition = image_partition;
5077 
5078   /* Run the command.  */
5079   if (! install_func (cmd_arg, flags))
5080     grub_printf ("succeeded\nDone.\n");
5081   else
5082     grub_printf ("failed\n");
5083 
5084  fail:
5085   saved_drive = tmp_drive;
5086   saved_partition = tmp_partition;
5087   return errnum;
5088 }
5089 
5090 static struct builtin builtin_setup =
5091 {
5092   "setup",
5093   setup_func,
5094   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
5095   "setup [--prefix=DIR] [--stage2=STAGE2_FILE] [--force-lba] INSTALL_DEVICE [IMAGE_DEVICE]",
5096   "Set up the installation of GRUB automatically. This command uses"
5097   " the more flexible command \"install\" in the backend and installs"
5098   " GRUB into the device INSTALL_DEVICE. If IMAGE_DEVICE is specified,"
5099   " then find the GRUB images in the device IMAGE_DEVICE, otherwise"
5100   " use the current \"root device\", which can be set by the command"
5101   " \"root\". If you know that your BIOS should support LBA but GRUB"
5102   " doesn't work in LBA mode, specify the option `--force-lba'."
5103   " If you install GRUB under the grub shell and you cannot unmount the"
5104   " partition where GRUB images reside, specify the option `--stage2'"
5105   " to tell GRUB the file name under your OS."
5106 };
5107 
5108 
5109 #if defined(SUPPORT_SERIAL) || defined(SUPPORT_HERCULES) || defined(SUPPORT_GRAPHICS)
5110 /* terminal */
5111 static int
5112 terminal_func (char *arg, int flags)
5113 {
5114   /* The index of the default terminal in TERM_TABLE.  */
5115   int default_term = -1;
5116   struct term_entry *prev_term = current_term;
5117   int to = -1;
5118   int lines = 0;
5119   int no_message = 0;
5120   unsigned long term_flags = 0;
5121   /* XXX: Assume less than 32 terminals.  */
5122   unsigned long term_bitmap = 0;
5123 
5124   /* Get GNU-style long options.  */
5125   while (1)
5126     {
5127       if (grub_memcmp (arg, "--dumb", sizeof ("--dumb") - 1) == 0)
5128 	term_flags |= TERM_DUMB;
5129       else if (grub_memcmp (arg, "--no-echo", sizeof ("--no-echo") - 1) == 0)
5130 	/* ``--no-echo'' implies ``--no-edit''.  */
5131 	term_flags |= (TERM_NO_ECHO | TERM_NO_EDIT);
5132       else if (grub_memcmp (arg, "--no-edit", sizeof ("--no-edit") - 1) == 0)
5133 	term_flags |= TERM_NO_EDIT;
5134       else if (grub_memcmp (arg, "--timeout=", sizeof ("--timeout=") - 1) == 0)
5135 	{
5136 	  char *val = arg + sizeof ("--timeout=") - 1;
5137 
5138 	  if (! safe_parse_maxint (&val, &to))
5139 	    return 1;
5140 	}
5141       else if (grub_memcmp (arg, "--lines=", sizeof ("--lines=") - 1) == 0)
5142 	{
5143 	  char *val = arg + sizeof ("--lines=") - 1;
5144 
5145 	  if (! safe_parse_maxint (&val, &lines))
5146 	    return 1;
5147 
5148 	  /* Probably less than four is meaningless....  */
5149 	  if (lines < 4)
5150 	    {
5151 	      errnum = ERR_BAD_ARGUMENT;
5152 	      return 1;
5153 	    }
5154 	}
5155       else if (grub_memcmp (arg, "--silent", sizeof ("--silent") - 1) == 0)
5156 	no_message = 1;
5157       else
5158 	break;
5159 
5160       arg = skip_to (0, arg);
5161     }
5162 
5163   /* If no argument is specified, show current setting.  */
5164   if (! *arg)
5165     {
5166       grub_printf ("%s%s%s%s\n",
5167 		   current_term->name,
5168 		   current_term->flags & TERM_DUMB ? " (dumb)" : "",
5169 		   current_term->flags & TERM_NO_EDIT ? " (no edit)" : "",
5170 		   current_term->flags & TERM_NO_ECHO ? " (no echo)" : "");
5171       return 0;
5172     }
5173 
5174   while (*arg)
5175     {
5176       int i;
5177       char *next = skip_to (0, arg);
5178 
5179       nul_terminate (arg);
5180 
5181       for (i = 0; term_table[i].name; i++)
5182 	{
5183 	  if (grub_strcmp (arg, term_table[i].name) == 0)
5184 	    {
5185 	      if (term_table[i].flags & TERM_NEED_INIT)
5186 		{
5187 		  errnum = ERR_DEV_NEED_INIT;
5188 		  return 1;
5189 		}
5190 
5191 	      if (default_term < 0)
5192 		default_term = i;
5193 
5194 	      term_bitmap |= (1 << i);
5195 	      break;
5196 	    }
5197 	}
5198 
5199       if (! term_table[i].name)
5200 	{
5201 	  errnum = ERR_BAD_ARGUMENT;
5202 	  return 1;
5203 	}
5204 
5205       arg = next;
5206     }
5207 
5208   /* If multiple terminals are specified, wait until the user pushes any
5209      key on one of the terminals.  */
5210   if (term_bitmap & ~(1 << default_term))
5211     {
5212       int time1, time2 = -1;
5213 
5214       /* XXX: Disable the pager.  */
5215       count_lines = -1;
5216 
5217       /* Get current time.  */
5218       while ((time1 = getrtsecs ()) == 0xFF)
5219 	;
5220 
5221       /* Wait for a key input.  */
5222       while (to)
5223 	{
5224 	  int i;
5225 
5226 	  for (i = 0; term_table[i].name; i++)
5227 	    {
5228 	      if (term_bitmap & (1 << i))
5229 		{
5230 		  if (term_table[i].checkkey () >= 0)
5231 		    {
5232 		      (void) term_table[i].getkey ();
5233 		      default_term = i;
5234 
5235 		      goto end;
5236 		    }
5237 		}
5238 	    }
5239 
5240 	  /* Prompt the user, once per sec.  */
5241 	  if ((time1 = getrtsecs ()) != time2 && time1 != 0xFF)
5242 	    {
5243 	      if (! no_message)
5244 		{
5245 		  /* Need to set CURRENT_TERM to each of selected
5246 		     terminals.  */
5247 		  for (i = 0; term_table[i].name; i++)
5248 		    if (term_bitmap & (1 << i))
5249 		      {
5250 			current_term = term_table + i;
5251 			grub_printf ("\rPress any key to continue.\n");
5252 		      }
5253 
5254 		  /* Restore CURRENT_TERM.  */
5255 		  current_term = prev_term;
5256 		}
5257 
5258 	      time2 = time1;
5259 	      if (to > 0)
5260 		to--;
5261 	    }
5262 	}
5263     }
5264 
5265  end:
5266   current_term = term_table + default_term;
5267   current_term->flags = term_flags;
5268 
5269   if (lines)
5270     max_lines = lines;
5271   else
5272     max_lines = current_term->max_lines;
5273 
5274   /* If the interface is currently the command-line,
5275      restart it to repaint the screen.  */
5276   if ((current_term != prev_term) && (flags & BUILTIN_CMDLINE)){
5277     if (prev_term->shutdown)
5278       prev_term->shutdown();
5279     if (current_term->startup)
5280       current_term->startup();
5281     grub_longjmp (restart_cmdline_env, 0);
5282   }
5283 
5284   return 0;
5285 }
5286 
5287 static struct builtin builtin_terminal =
5288 {
5289   "terminal",
5290   terminal_func,
5291   BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
5292   "terminal [--dumb] [--no-echo] [--no-edit] [--timeout=SECS] [--lines=LINES] [--silent] [console] [serial] [hercules] [graphics]",
5293   "Select a terminal. When multiple terminals are specified, wait until"
5294   " you push any key to continue. If both console and serial are specified,"
5295   " the terminal to which you input a key first will be selected. If no"
5296   " argument is specified, print current setting. The option --dumb"
5297   " specifies that your terminal is dumb, otherwise, vt100-compatibility"
5298   " is assumed. If you specify --no-echo, input characters won't be echoed."
5299   " If you specify --no-edit, the BASH-like editing feature will be disabled."
5300   " If --timeout is present, this command will wait at most for SECS"
5301   " seconds. The option --lines specifies the maximum number of lines."
5302   " The option --silent is used to suppress messages."
5303 };
5304 #endif /* SUPPORT_SERIAL || SUPPORT_HERCULES || SUPPORT_GRAPHICS */
5305 
5306 
5307 #ifdef SUPPORT_SERIAL
5308 static int
5309 terminfo_func (char *arg, int flags)
5310 {
5311   struct terminfo term;
5312 
5313   if (*arg)
5314     {
5315       struct
5316       {
5317 	const char *name;
5318 	char *var;
5319       }
5320       options[] =
5321 	{
5322 	  {"--name=", term.name},
5323 	  {"--cursor-address=", term.cursor_address},
5324 	  {"--clear-screen=", term.clear_screen},
5325 	  {"--enter-standout-mode=", term.enter_standout_mode},
5326 	  {"--exit-standout-mode=", term.exit_standout_mode}
5327 	};
5328 
5329       grub_memset (&term, 0, sizeof (term));
5330 
5331       while (*arg)
5332 	{
5333 	  int i;
5334 	  char *next = skip_to (0, arg);
5335 
5336 	  nul_terminate (arg);
5337 
5338 	  for (i = 0; i < sizeof (options) / sizeof (options[0]); i++)
5339 	    {
5340 	      const char *name = options[i].name;
5341 	      int len = grub_strlen (name);
5342 
5343 	      if (! grub_memcmp (arg, name, len))
5344 		{
5345 		  grub_strcpy (options[i].var, ti_unescape_string (arg + len));
5346 		  break;
5347 		}
5348 	    }
5349 
5350 	  if (i == sizeof (options) / sizeof (options[0]))
5351 	    {
5352 	      errnum = ERR_BAD_ARGUMENT;
5353 	      return errnum;
5354 	    }
5355 
5356 	  arg = next;
5357 	}
5358 
5359       if (term.name[0] == 0 || term.cursor_address[0] == 0)
5360 	{
5361 	  errnum = ERR_BAD_ARGUMENT;
5362 	  return errnum;
5363 	}
5364 
5365       ti_set_term (&term);
5366     }
5367   else
5368     {
5369       /* No option specifies printing out current settings.  */
5370       ti_get_term (&term);
5371 
5372       grub_printf ("name=%s\n",
5373 		   ti_escape_string (term.name));
5374       grub_printf ("cursor_address=%s\n",
5375 		   ti_escape_string (term.cursor_address));
5376       grub_printf ("clear_screen=%s\n",
5377 		   ti_escape_string (term.clear_screen));
5378       grub_printf ("enter_standout_mode=%s\n",
5379 		   ti_escape_string (term.enter_standout_mode));
5380       grub_printf ("exit_standout_mode=%s\n",
5381 		   ti_escape_string (term.exit_standout_mode));
5382     }
5383 
5384   return 0;
5385 }
5386 
5387 static struct builtin builtin_terminfo =
5388 {
5389   "terminfo",
5390   terminfo_func,
5391   BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
5392   "terminfo [--name=NAME --cursor-address=SEQ [--clear-screen=SEQ]"
5393   " [--enter-standout-mode=SEQ] [--exit-standout-mode=SEQ]]",
5394 
5395   "Define the capabilities of your terminal. Use this command to"
5396   " define escape sequences, if it is not vt100-compatible."
5397   " You may use \\e for ESC and ^X for a control character."
5398   " If no option is specified, the current settings are printed."
5399 };
5400 #endif /* SUPPORT_SERIAL */
5401 
5402 
5403 /* testload */
5404 static int
5405 testload_func (char *arg, int flags)
5406 {
5407   int i;
5408 
5409   kernel_type = KERNEL_TYPE_NONE;
5410 
5411   if (! grub_open (arg))
5412     return 1;
5413 
5414   disk_read_hook = disk_read_print_func;
5415 
5416   /* Perform filesystem test on the specified file.  */
5417   /* Read whole file first. */
5418   grub_printf ("Whole file: ");
5419 
5420   grub_read ((char *) RAW_ADDR (0x100000), -1);
5421 
5422   /* Now compare two sections of the file read differently.  */
5423 
5424   for (i = 0; i < 0x10ac0; i++)
5425     {
5426       *((unsigned char *) RAW_ADDR (0x200000 + i)) = 0;
5427       *((unsigned char *) RAW_ADDR (0x300000 + i)) = 1;
5428     }
5429 
5430   /* First partial read.  */
5431   grub_printf ("\nPartial read 1: ");
5432 
5433   grub_seek (0);
5434   grub_read ((char *) RAW_ADDR (0x200000), 0x7);
5435   grub_read ((char *) RAW_ADDR (0x200007), 0x100);
5436   grub_read ((char *) RAW_ADDR (0x200107), 0x10);
5437   grub_read ((char *) RAW_ADDR (0x200117), 0x999);
5438   grub_read ((char *) RAW_ADDR (0x200ab0), 0x10);
5439   grub_read ((char *) RAW_ADDR (0x200ac0), 0x10000);
5440 
5441   /* Second partial read.  */
5442   grub_printf ("\nPartial read 2: ");
5443 
5444   grub_seek (0);
5445   grub_read ((char *) RAW_ADDR (0x300000), 0x10000);
5446   grub_read ((char *) RAW_ADDR (0x310000), 0x10);
5447   grub_read ((char *) RAW_ADDR (0x310010), 0x7);
5448   grub_read ((char *) RAW_ADDR (0x310017), 0x10);
5449   grub_read ((char *) RAW_ADDR (0x310027), 0x999);
5450   grub_read ((char *) RAW_ADDR (0x3109c0), 0x100);
5451 
5452   grub_printf ("\nHeader1 = 0x%x, next = 0x%x, next = 0x%x, next = 0x%x\n",
5453 	       *((int *) RAW_ADDR (0x200000)),
5454 	       *((int *) RAW_ADDR (0x200004)),
5455 	       *((int *) RAW_ADDR (0x200008)),
5456 	       *((int *) RAW_ADDR (0x20000c)));
5457 
5458   grub_printf ("Header2 = 0x%x, next = 0x%x, next = 0x%x, next = 0x%x\n",
5459 	       *((int *) RAW_ADDR (0x300000)),
5460 	       *((int *) RAW_ADDR (0x300004)),
5461 	       *((int *) RAW_ADDR (0x300008)),
5462 	       *((int *) RAW_ADDR (0x30000c)));
5463 
5464   for (i = 0; i < 0x10ac0; i++)
5465     if (*((unsigned char *) RAW_ADDR (0x200000 + i))
5466 	!= *((unsigned char *) RAW_ADDR (0x300000 + i)))
5467       break;
5468 
5469   grub_printf ("Max is 0x10ac0: i=0x%x, filepos=0x%x\n", i, filepos);
5470   disk_read_hook = 0;
5471   grub_close ();
5472   return 0;
5473 }
5474 
5475 static struct builtin builtin_testload =
5476 {
5477   "testload",
5478   testload_func,
5479   BUILTIN_CMDLINE,
5480   "testload FILE",
5481   "Read the entire contents of FILE in several different ways and"
5482   " compares them, to test the filesystem code. The output is somewhat"
5483   " cryptic, but if no errors are reported and the final `i=X,"
5484   " filepos=Y' reading has X and Y equal, then it is definitely"
5485   " consistent, and very likely works correctly subject to a"
5486   " consistent offset error. If this test succeeds, then a good next"
5487   " step is to try loading a kernel."
5488 };
5489 
5490 
5491 /* testvbe MODE */
5492 static int
5493 testvbe_func (char *arg, int flags)
5494 {
5495   int mode_number;
5496   struct vbe_controller controller;
5497   struct vbe_mode mode;
5498 
5499   if (! *arg)
5500     {
5501       errnum = ERR_BAD_ARGUMENT;
5502       return 1;
5503     }
5504 
5505   if (! safe_parse_maxint (&arg, &mode_number))
5506     return 1;
5507 
5508   /* Preset `VBE2'.  */
5509   grub_memmove (controller.signature, "VBE2", 4);
5510 
5511   /* Detect VBE BIOS.  */
5512   if (get_vbe_controller_info (&controller) != 0x004F)
5513     {
5514       grub_printf (" VBE BIOS is not present.\n");
5515       return 0;
5516     }
5517 
5518   if (controller.version < 0x0200)
5519     {
5520       grub_printf (" VBE version %d.%d is not supported.\n",
5521 		   (int) (controller.version >> 8),
5522 		   (int) (controller.version & 0xFF));
5523       return 0;
5524     }
5525 
5526   if (get_vbe_mode_info (mode_number, &mode) != 0x004F
5527       || (mode.mode_attributes & 0x0091) != 0x0091)
5528     {
5529       grub_printf (" Mode 0x%x is not supported.\n", mode_number);
5530       return 0;
5531     }
5532 
5533   /* Now trip to the graphics mode.  */
5534   if (set_vbe_mode (mode_number | (1 << 14)) != 0x004F)
5535     {
5536       grub_printf (" Switching to Mode 0x%x failed.\n", mode_number);
5537       return 0;
5538     }
5539 
5540   /* Draw something on the screen...  */
5541   {
5542     unsigned char *base_buf = (unsigned char *) mode.phys_base;
5543     int scanline = controller.version >= 0x0300
5544       ? mode.linear_bytes_per_scanline : mode.bytes_per_scanline;
5545     /* FIXME: this assumes that any depth is a modulo of 8.  */
5546     int bpp = mode.bits_per_pixel / 8;
5547     int width = mode.x_resolution;
5548     int height = mode.y_resolution;
5549     int x, y;
5550     unsigned color = 0;
5551 
5552     /* Iterate drawing on the screen, until the user hits any key.  */
5553     while (checkkey () == -1)
5554       {
5555 	for (y = 0; y < height; y++)
5556 	  {
5557 	    unsigned char *line_buf = base_buf + scanline * y;
5558 
5559 	    for (x = 0; x < width; x++)
5560 	      {
5561 		unsigned char *buf = line_buf + bpp * x;
5562 		int i;
5563 
5564 		for (i = 0; i < bpp; i++, buf++)
5565 		  *buf = (color >> (i * 8)) & 0xff;
5566 	      }
5567 
5568 	    color++;
5569 	  }
5570       }
5571 
5572     /* Discard the input.  */
5573     getkey ();
5574   }
5575 
5576   /* Back to the default text mode.  */
5577   if (set_vbe_mode (0x03) != 0x004F)
5578     {
5579       /* Why?!  */
5580       grub_reboot ();
5581     }
5582 
5583   return 0;
5584 }
5585 
5586 static struct builtin builtin_testvbe =
5587 {
5588   "testvbe",
5589   testvbe_func,
5590   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
5591   "testvbe MODE",
5592   "Test the VBE mode MODE. Hit any key to return."
5593 };
5594 
5595 
5596 #ifdef SUPPORT_NETBOOT
5597 /* tftpserver */
5598 static int
5599 tftpserver_func (char *arg, int flags)
5600 {
5601   if (! *arg || ! ifconfig (0, 0, 0, arg))
5602     {
5603       errnum = ERR_BAD_ARGUMENT;
5604       return 1;
5605     }
5606 
5607   print_network_configuration ();
5608   return 0;
5609 }
5610 
5611 static struct builtin builtin_tftpserver =
5612 {
5613   "tftpserver",
5614   tftpserver_func,
5615   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
5616   "tftpserver IPADDR",
5617   "Override the TFTP server address."
5618 };
5619 #endif /* SUPPORT_NETBOOT */
5620 
5621 
5622 /* timeout */
5623 static int
5624 timeout_func (char *arg, int flags)
5625 {
5626   if (! safe_parse_maxint (&arg, &grub_timeout))
5627     return 1;
5628 
5629   return 0;
5630 }
5631 
5632 static struct builtin builtin_timeout =
5633 {
5634   "timeout",
5635   timeout_func,
5636   BUILTIN_MENU,
5637 #if 0
5638   "timeout SEC",
5639   "Set a timeout, in SEC seconds, before automatically booting the"
5640   " default entry (normally the first entry defined)."
5641 #endif
5642 };
5643 
5644 
5645 /* title */
5646 static int
5647 title_func (char *arg, int flags)
5648 {
5649   /* This function is not actually used at least currently.  */
5650   return 0;
5651 }
5652 
5653 static struct builtin builtin_title =
5654 {
5655   "title",
5656   title_func,
5657   BUILTIN_TITLE,
5658 #if 0
5659   "title [NAME ...]",
5660   "Start a new boot entry, and set its name to the contents of the"
5661   " rest of the line, starting with the first non-space character."
5662 #endif
5663 };
5664 
5665 
5666 /* unhide */
5667 static int
5668 unhide_func (char *arg, int flags)
5669 {
5670   if (! set_device (arg))
5671     return 1;
5672 
5673   if (! set_partition_hidden_flag (0))
5674     return 1;
5675 
5676   return 0;
5677 }
5678 
5679 static struct builtin builtin_unhide =
5680 {
5681   "unhide",
5682   unhide_func,
5683   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
5684   "unhide PARTITION",
5685   "Unhide PARTITION by clearing the \"hidden\" bit in its"
5686   " partition type code."
5687 };
5688 
5689 
5690 /* uppermem */
5691 static int
5692 uppermem_func (char *arg, int flags)
5693 {
5694   if (! safe_parse_maxint (&arg, (int *) &mbi.mem_upper))
5695     return 1;
5696 
5697   mbi.flags &= ~MB_INFO_MEM_MAP;
5698   return 0;
5699 }
5700 
5701 static struct builtin builtin_uppermem =
5702 {
5703   "uppermem",
5704   uppermem_func,
5705   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
5706   "uppermem KBYTES",
5707   "Force GRUB to assume that only KBYTES kilobytes of upper memory are"
5708   " installed.  Any system address range maps are discarded."
5709 };
5710 
5711 
5712 /* vbeprobe */
5713 static int
5714 vbeprobe_func (char *arg, int flags)
5715 {
5716   struct vbe_controller controller;
5717   unsigned short *mode_list;
5718   int mode_number = -1;
5719 
5720   auto unsigned long vbe_far_ptr_to_linear (unsigned long);
5721 
5722   unsigned long vbe_far_ptr_to_linear (unsigned long ptr)
5723     {
5724       unsigned short seg = (ptr >> 16);
5725       unsigned short off = (ptr & 0xFFFF);
5726 
5727       return (seg << 4) + off;
5728     }
5729 
5730   if (*arg)
5731     {
5732       if (! safe_parse_maxint (&arg, &mode_number))
5733 	return 1;
5734     }
5735 
5736   /* Set the signature to `VBE2', to obtain VBE 3.0 information.  */
5737   grub_memmove (controller.signature, "VBE2", 4);
5738 
5739   if (get_vbe_controller_info (&controller) != 0x004F)
5740     {
5741       grub_printf (" VBE BIOS is not present.\n");
5742       return 0;
5743     }
5744 
5745   /* Check the version.  */
5746   if (controller.version < 0x0200)
5747     {
5748       grub_printf (" VBE version %d.%d is not supported.\n",
5749 		   (int) (controller.version >> 8),
5750 		   (int) (controller.version & 0xFF));
5751       return 0;
5752     }
5753 
5754   /* Print some information.  */
5755   grub_printf (" VBE version %d.%d\n",
5756 	       (int) (controller.version >> 8),
5757 	       (int) (controller.version & 0xFF));
5758 
5759   /* Iterate probing modes.  */
5760   for (mode_list
5761 	 = (unsigned short *) vbe_far_ptr_to_linear (controller.video_mode);
5762        *mode_list != 0xFFFF;
5763        mode_list++)
5764     {
5765       struct vbe_mode mode;
5766 
5767       if (get_vbe_mode_info (*mode_list, &mode) != 0x004F)
5768 	continue;
5769 
5770       /* Skip this, if this is not supported or linear frame buffer
5771 	 mode is not support.  */
5772       if ((mode.mode_attributes & 0x0081) != 0x0081)
5773 	continue;
5774 
5775       if (mode_number == -1 || mode_number == *mode_list)
5776 	{
5777 	  char *model;
5778 	  switch (mode.memory_model)
5779 	    {
5780 	    case 0x00: model = "Text"; break;
5781 	    case 0x01: model = "CGA graphics"; break;
5782 	    case 0x02: model = "Hercules graphics"; break;
5783 	    case 0x03: model = "Planar"; break;
5784 	    case 0x04: model = "Packed pixel"; break;
5785 	    case 0x05: model = "Non-chain 4, 256 color"; break;
5786 	    case 0x06: model = "Direct Color"; break;
5787 	    case 0x07: model = "YUV"; break;
5788 	    default: model = "Unknown"; break;
5789 	    }
5790 
5791 	  grub_printf ("  0x%x: %s, %ux%ux%u\n",
5792 		       (unsigned) *mode_list,
5793 		       model,
5794 		       (unsigned) mode.x_resolution,
5795 		       (unsigned) mode.y_resolution,
5796 		       (unsigned) mode.bits_per_pixel);
5797 
5798 	  if (mode_number != -1)
5799 	    break;
5800 	}
5801     }
5802 
5803   if (mode_number != -1 && mode_number != *mode_list)
5804     grub_printf ("  Mode 0x%x is not found or supported.\n", mode_number);
5805 
5806   return 0;
5807 }
5808 
5809 static struct builtin builtin_vbeprobe =
5810 {
5811   "vbeprobe",
5812   vbeprobe_func,
5813   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
5814   "vbeprobe [MODE]",
5815   "Probe VBE information. If the mode number MODE is specified, show only"
5816   " the information about only the mode."
5817 };
5818 
5819 
5820 /* The table of builtin commands. Sorted in dictionary order.  */
5821 struct builtin *builtin_table[] =
5822 {
5823 #ifdef SUPPORT_GRAPHICS
5824   &builtin_background,
5825 #endif
5826   &builtin_blocklist,
5827   &builtin_boot,
5828   &builtin_bootfs,
5829 #ifdef SUPPORT_NETBOOT
5830   &builtin_bootp,
5831 #endif /* SUPPORT_NETBOOT */
5832   &builtin_cat,
5833   &builtin_chainloader,
5834   &builtin_clear,
5835   &builtin_cmp,
5836   &builtin_color,
5837   &builtin_configfile,
5838   &builtin_debug,
5839   &builtin_default,
5840 #ifdef GRUB_UTIL
5841   &builtin_device,
5842 #endif /* GRUB_UTIL */
5843 #ifdef SUPPORT_NETBOOT
5844   &builtin_dhcp,
5845 #endif /* SUPPORT_NETBOOT */
5846   &builtin_displayapm,
5847   &builtin_displaymem,
5848 #ifdef GRUB_UTIL
5849   &builtin_dump,
5850 #endif /* GRUB_UTIL */
5851   &builtin_embed,
5852   &builtin_fallback,
5853   &builtin_find,
5854   &builtin_findroot,
5855 #ifdef SUPPORT_GRAPHICS
5856   &builtin_foreground,
5857 #endif
5858   &builtin_fstest,
5859   &builtin_geometry,
5860   &builtin_halt,
5861   &builtin_help,
5862   &builtin_hiddenmenu,
5863   &builtin_hide,
5864 #ifdef SUPPORT_NETBOOT
5865   &builtin_ifconfig,
5866 #endif /* SUPPORT_NETBOOT */
5867   &builtin_impsprobe,
5868   &builtin_initrd,
5869   &builtin_install,
5870   &builtin_ioprobe,
5871   &builtin_kernel,
5872   &builtin_kernel_dollar,
5873   &builtin_lock,
5874   &builtin_makeactive,
5875   &builtin_map,
5876 #ifdef USE_MD5_PASSWORDS
5877   &builtin_md5crypt,
5878 #endif /* USE_MD5_PASSWORDS */
5879   &builtin_min_mem64,
5880   &builtin_module,
5881   &builtin_module_dollar,
5882   &builtin_modulenounzip,
5883   &builtin_pager,
5884   &builtin_partnew,
5885   &builtin_parttype,
5886   &builtin_password,
5887   &builtin_pause,
5888 #if defined(RPC_DEBUG) && defined(SUPPORT_NETBOOT)
5889   &builtin_portmap,
5890 #endif /* RPC_DEBUG && SUPPORT_NETBOOT */
5891 #ifdef GRUB_UTIL
5892   &builtin_quit,
5893 #endif /* GRUB_UTIL */
5894 #ifdef SUPPORT_NETBOOT
5895   &builtin_rarp,
5896 #endif /* SUPPORT_NETBOOT */
5897   &builtin_read,
5898   &builtin_reboot,
5899   &builtin_root,
5900   &builtin_rootnoverify,
5901   &builtin_savedefault,
5902 #ifdef SUPPORT_SERIAL
5903   &builtin_serial,
5904 #endif /* SUPPORT_SERIAL */
5905   &builtin_setkey,
5906   &builtin_setup,
5907 #ifdef SUPPORT_GRAPHICS
5908   &builtin_splashimage,
5909 #endif /* SUPPORT_GRAPHICS */
5910 #if defined(SUPPORT_SERIAL) || defined(SUPPORT_HERCULES) || defined(SUPPORT_GRAPHICS)
5911   &builtin_terminal,
5912 #endif /* SUPPORT_SERIAL || SUPPORT_HERCULES || SUPPORT_GRAPHICS */
5913 #ifdef SUPPORT_SERIAL
5914   &builtin_terminfo,
5915 #endif /* SUPPORT_SERIAL */
5916   &builtin_testload,
5917   &builtin_testvbe,
5918 #ifdef SUPPORT_NETBOOT
5919   &builtin_tftpserver,
5920 #endif /* SUPPORT_NETBOOT */
5921   &builtin_timeout,
5922   &builtin_title,
5923   &builtin_unhide,
5924   &builtin_uppermem,
5925   &builtin_vbeprobe,
5926   &builtin_verbose,
5927   0
5928 };
5929