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