xref: /illumos-gate/usr/src/grub/grub-0.97/stage2/builtins.c (revision 38614cc7545c91d5a15f18afb63641c7ed357895)
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 	/* no op if this is not zfs */
2777 	if (is_zfs_mount == 0)
2778 		return (0);
2779 
2780 	if (current_bootpath[0] == '\0' && current_devid[0] == '\0') {
2781 		errnum = ERR_NO_BOOTPATH;
2782 		return (1);
2783 	}
2784 
2785 	outlen = strlen(in);
2786 	blen = current_bootfs_obj == 0 ? strlen(current_rootpool) :
2787 	    strlen(current_rootpool) + 11;
2788 
2789 	out[0] = '\0';
2790 	while (token = strstr(in, "$ZFS-BOOTFS")) {
2791 
2792 		if ((outlen += blen) >= MAX_CMDLINE) {
2793 			errnum = ERR_WONT_FIT;
2794 			return (1);
2795 		}
2796 
2797 		token[0] = '\0';
2798 		grub_sprintf(tmpout, "%s", in);
2799 		token[0] = '$';
2800 		in = token + 11; /* skip over $ZFS-BOOTFS */
2801 		tmpout = out + strlen(out);
2802 
2803 		/* Note: %u only fits 32 bit integer; */
2804 		if (current_bootfs_obj > 0)
2805 			grub_sprintf(tmpout, "zfs-bootfs=%s/%u",
2806 			    current_rootpool, current_bootfs_obj);
2807 		else
2808 			grub_sprintf(tmpout, "zfs-bootfs=%s",
2809 			    current_rootpool);
2810 		tmpout = out + strlen(out);
2811 	}
2812 
2813 	/*
2814 	 * Check to see if 'zfs-bootfs' was explicitly specified on the command
2815 	 * line so that we can insert the 'bootpath' property.
2816 	 */
2817 	if ((tmpout == out) && (token = strstr(in, "zfs-bootfs")) != NULL) {
2818 		token[0] = '\0';
2819 		grub_strcpy(tmpout, in);
2820 		token[0] = 'z';
2821 		in = token;
2822 
2823 		tmpout = out + strlen(out);
2824 		postcomma = 1;
2825 	}
2826 
2827 	/*
2828 	 * Set the 'bootpath' property if a ZFS dataset was specified, either
2829 	 * through '$ZFS-BOOTFS' or an explicit 'zfs-bootfs' setting.
2830 	 */
2831 	if (tmpout != out) {
2832 		if (current_bootpath[0] != '\0') {
2833 			if ((outlen += 12 + strlen(current_bootpath))
2834 			    >= MAX_CMDLINE) {
2835 				errnum = ERR_WONT_FIT;
2836 				return (1);
2837 			}
2838 			grub_sprintf(tmpout,
2839 			    postcomma ? "bootpath=\"%s\"," : ",bootpath=\"%s\"",
2840 			    current_bootpath);
2841 			tmpout = out + strlen(out);
2842 		}
2843 
2844 		if (current_devid[0] != '\0') {
2845 			if ((outlen += 13 + strlen(current_devid))
2846 			    >= MAX_CMDLINE) {
2847 				errnum = ERR_WONT_FIT;
2848 				return (1);
2849 			}
2850 			grub_sprintf(tmpout,
2851 			    postcomma ? "diskdevid=\"%s\"," : ",diskdevid=\"%s\"",
2852 			    current_devid);
2853 		}
2854 	}
2855 
2856 	strncat(out, in, MAX_CMDLINE);
2857 	return (0);
2858 }
2859 
2860 /* kernel */
2861 static int
2862 kernel_func (char *arg, int flags)
2863 {
2864   int len;
2865   kernel_t suggested_type = KERNEL_TYPE_NONE;
2866   unsigned long load_flags = 0;
2867 
2868 #ifndef AUTO_LINUX_MEM_OPT
2869   load_flags |= KERNEL_LOAD_NO_MEM_OPTION;
2870 #endif
2871 
2872   /* Deal with GNU-style long options.  */
2873   while (1)
2874     {
2875       /* If the option `--type=TYPE' is specified, convert the string to
2876 	 a kernel type.  */
2877       if (grub_memcmp (arg, "--type=", 7) == 0)
2878 	{
2879 	  arg += 7;
2880 
2881 	  if (grub_memcmp (arg, "netbsd", 6) == 0)
2882 	    suggested_type = KERNEL_TYPE_NETBSD;
2883 	  else if (grub_memcmp (arg, "freebsd", 7) == 0)
2884 	    suggested_type = KERNEL_TYPE_FREEBSD;
2885 	  else if (grub_memcmp (arg, "openbsd", 7) == 0)
2886 	    /* XXX: For now, OpenBSD is identical to NetBSD, from GRUB's
2887 	       point of view.  */
2888 	    suggested_type = KERNEL_TYPE_NETBSD;
2889 	  else if (grub_memcmp (arg, "linux", 5) == 0)
2890 	    suggested_type = KERNEL_TYPE_LINUX;
2891 	  else if (grub_memcmp (arg, "biglinux", 8) == 0)
2892 	    suggested_type = KERNEL_TYPE_BIG_LINUX;
2893 	  else if (grub_memcmp (arg, "multiboot", 9) == 0)
2894 	    suggested_type = KERNEL_TYPE_MULTIBOOT;
2895 	  else
2896 	    {
2897 	      errnum = ERR_BAD_ARGUMENT;
2898 	      return 1;
2899 	    }
2900 	}
2901       /* If the `--no-mem-option' is specified, don't pass a Linux's mem
2902 	 option automatically. If the kernel is another type, this flag
2903 	 has no effect.  */
2904       else if (grub_memcmp (arg, "--no-mem-option", 15) == 0)
2905 	load_flags |= KERNEL_LOAD_NO_MEM_OPTION;
2906       else
2907 	break;
2908 
2909       /* Try the next.  */
2910       arg = skip_to (0, arg);
2911     }
2912 
2913   len = grub_strlen (arg);
2914 
2915   /* Reset MB_CMDLINE.  */
2916   mb_cmdline = (char *) MB_CMDLINE_BUF;
2917   if (len + 1 > MB_CMDLINE_BUFLEN)
2918     {
2919       errnum = ERR_WONT_FIT;
2920       return 1;
2921     }
2922 
2923   /* Copy the command-line to MB_CMDLINE.  */
2924   grub_memmove (mb_cmdline, arg, len + 1);
2925   kernel_type = load_image (arg, mb_cmdline, suggested_type, load_flags);
2926   if (kernel_type == KERNEL_TYPE_NONE)
2927     return 1;
2928 
2929   mb_cmdline += grub_strlen(mb_cmdline) + 1;
2930   return 0;
2931 }
2932 
2933 static struct builtin builtin_kernel =
2934 {
2935   "kernel",
2936   kernel_func,
2937   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
2938   "kernel [--no-mem-option] [--type=TYPE] FILE [ARG ...]",
2939   "Attempt to load the primary boot image from FILE. The rest of the"
2940   " line is passed verbatim as the \"kernel command line\".  Any modules"
2941   " must be reloaded after using this command. The option --type is used"
2942   " to suggest what type of kernel to be loaded. TYPE must be either of"
2943   " \"netbsd\", \"freebsd\", \"openbsd\", \"linux\", \"biglinux\" and"
2944   " \"multiboot\". The option --no-mem-option tells GRUB not to pass a"
2945   " Linux's mem option automatically."
2946 };
2947 
2948 int
2949 min_mem64_func(char *arg, int flags)
2950 {
2951 	if (!safe_parse_maxint(&arg, &min_mem64))
2952 		return (1);
2953 }
2954 
2955 static struct builtin builtin_min_mem64 =
2956 {
2957 	"min_mem64",
2958 	min_mem64_func,
2959 	BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_SCRIPT | BUILTIN_HELP_LIST,
2960 	"min_mem64 <memory in MB>",
2961 	"Sets minimum memory (in MB) required for $ISADIR to expand to amd64, "
2962 	"even on 64-bit capable hardware."
2963 };
2964 
2965 int
2966 check_min_mem64()
2967 {
2968 	if (min_mem64 == 0)
2969 		return (1);
2970 
2971 	if ((mbi.mem_upper / 10240) * 11 >= min_mem64)
2972 		return (1);
2973 
2974 	return (0);
2975 }
2976 
2977 static int detect_target_operating_mode();
2978 
2979 int
2980 amd64_config_cpu(void)
2981 {
2982         struct amd64_cpuid_regs __vcr, *vcr = &__vcr;
2983         uint32_t maxeax;
2984         uint32_t max_maxeax = 0x100;
2985         char vendor[13];
2986         int isamd64 = 0;
2987         uint32_t stdfeatures = 0, xtdfeatures = 0;
2988         uint64_t efer;
2989 
2990         /*
2991          * This check may seem silly, but if the C preprocesor symbol __amd64
2992          * is #defined during compilation, something that may outwardly seem
2993          * like a good idea, uts/common/sys/isa_defs.h will #define _LP64,
2994          * which will cause uts/common/sys/int_types.h to typedef uint64_t as
2995          * an unsigned long - which is only 4 bytes in size when using a 32-bit
2996          * compiler.
2997          *
2998          * If that happens, all the page table translation routines will fail
2999          * horribly, so check the size of uint64_t just to insure some degree
3000          * of sanity in future operations.
3001          */
3002         /*LINTED [sizeof result is invarient]*/
3003         if (sizeof (uint64_t) != 8)
3004                 prom_panic("grub compiled improperly, unable to boot "
3005                     "64-bit AMD64 executables");
3006 
3007         /*
3008          * If the CPU doesn't support the CPUID instruction, it's definitely
3009          * not an AMD64.
3010          */
3011         if (amd64_cpuid_supported() == 0)
3012                 return (0);
3013 
3014         amd64_cpuid_insn(0, vcr);
3015 
3016         maxeax = vcr->r_eax;
3017         {
3018                 /*LINTED [vendor string from cpuid data]*/
3019                 uint32_t *iptr = (uint32_t *)vendor;
3020 
3021                 *iptr++ = vcr->r_ebx;
3022                 *iptr++ = vcr->r_edx;
3023                 *iptr++ = vcr->r_ecx;
3024 
3025                 vendor[12] = '\0';
3026         }
3027 
3028         if (maxeax > max_maxeax) {
3029                 grub_printf("cpu: warning, maxeax was 0x%x -> 0x%x\n",
3030                     maxeax, max_maxeax);
3031                 maxeax = max_maxeax;
3032         }
3033 
3034         if (maxeax < 1)
3035                 return (0);     /* no additional functions, not an AMD64 */
3036         else {
3037                 uint_t family, model, step;
3038 
3039                 amd64_cpuid_insn(1, vcr);
3040 
3041                 /*
3042                  * All AMD64/IA32e processors technically SHOULD report
3043                  * themselves as being in family 0xf, but for some reason
3044                  * Simics doesn't, and this may change in the future, so
3045                  * don't error out if it's not true.
3046                  */
3047                 if ((family = BITX(vcr->r_eax, 11, 8)) == 0xf)
3048                         family += BITX(vcr->r_eax, 27, 20);
3049 
3050                 if ((model = BITX(vcr->r_eax, 7, 4)) == 0xf)
3051                         model += BITX(vcr->r_eax, 19, 16) << 4;
3052                 step = BITX(vcr->r_eax, 3, 0);
3053 
3054                 grub_printf("cpu: '%s' family %d model %d step %d\n",
3055                     vendor, family, model, step);
3056                 stdfeatures = vcr->r_edx;
3057         }
3058 
3059         amd64_cpuid_insn(0x80000000, vcr);
3060 
3061         if (vcr->r_eax & 0x80000000) {
3062                 uint32_t xmaxeax = vcr->r_eax;
3063                 const uint32_t max_xmaxeax = 0x80000100;
3064 
3065                 if (xmaxeax > max_xmaxeax) {
3066                         grub_printf("amd64: warning, xmaxeax was "
3067 			    "0x%x -> 0x%x\n", xmaxeax, max_xmaxeax);
3068                         xmaxeax = max_xmaxeax;
3069                 }
3070 
3071                 if (xmaxeax >= 0x80000001) {
3072                         amd64_cpuid_insn(0x80000001, vcr);
3073                         xtdfeatures = vcr->r_edx;
3074                 }
3075         }
3076 
3077         if (BITX(xtdfeatures, 29, 29))          /* long mode */
3078                 isamd64++;
3079         else
3080                 grub_printf("amd64: CPU does NOT support long mode\n");
3081 
3082         if (!BITX(stdfeatures, 0, 0)) {
3083                 grub_printf("amd64: CPU does NOT support FPU\n");
3084                 isamd64--;
3085         }
3086 
3087         if (!BITX(stdfeatures, 4, 4)) {
3088                 grub_printf("amd64: CPU does NOT support TSC\n");
3089                 isamd64--;
3090         }
3091 
3092         if (!BITX(stdfeatures, 5, 5)) {
3093                 grub_printf("amd64: CPU does NOT support MSRs\n");
3094                 isamd64--;
3095         }
3096 
3097         if (!BITX(stdfeatures, 6, 6)) {
3098                 grub_printf("amd64: CPU does NOT support PAE\n");
3099                 isamd64--;
3100         }
3101 
3102         if (!BITX(stdfeatures, 8, 8)) {
3103                 grub_printf("amd64: CPU does NOT support CX8\n");
3104                 isamd64--;
3105         }
3106 
3107         if (!BITX(stdfeatures, 13, 13)) {
3108                 grub_printf("amd64: CPU does NOT support PGE\n");
3109                 isamd64--;
3110         }
3111 
3112         if (!BITX(stdfeatures, 19, 19)) {
3113                 grub_printf("amd64: CPU does NOT support CLFSH\n");
3114                 isamd64--;
3115         }
3116 
3117         if (!BITX(stdfeatures, 23, 23)) {
3118                 grub_printf("amd64: CPU does NOT support MMX\n");
3119                 isamd64--;
3120         }
3121 
3122         if (!BITX(stdfeatures, 24, 24)) {
3123                 grub_printf("amd64: CPU does NOT support FXSR\n");
3124                 isamd64--;
3125         }
3126 
3127         if (!BITX(stdfeatures, 25, 25)) {
3128                 grub_printf("amd64: CPU does NOT support SSE\n");
3129                 isamd64--;
3130         }
3131 
3132         if (!BITX(stdfeatures, 26, 26)) {
3133                 grub_printf("amd64: CPU does NOT support SSE2\n");
3134                 isamd64--;
3135         }
3136 
3137         if (isamd64 < 1) {
3138                 grub_printf("amd64: CPU does not support amd64 executables.\n");
3139                 return (0);
3140         }
3141 
3142         amd64_rdmsr(MSR_AMD_EFER, &efer);
3143         if (efer & AMD_EFER_SCE)
3144                 grub_printf("amd64: EFER_SCE (syscall/sysret) already "
3145 		    "enabled\n");
3146         if (efer & AMD_EFER_NXE)
3147                 grub_printf("amd64: EFER_NXE (no-exec prot) already enabled\n");
3148         if (efer & AMD_EFER_LME)
3149                 grub_printf("amd64: EFER_LME (long mode) already enabled\n");
3150 
3151         return (detect_target_operating_mode());
3152 }
3153 
3154 static int
3155 detect_target_operating_mode()
3156 {
3157         int ret, ah;
3158 
3159 	ah = get_target_operating_mode();
3160 
3161         ah = ah >> 8;
3162 
3163 	/* XXX still need to pass back the return from the call  */
3164 	ret = 0;
3165 
3166         if (ah == 0x86 && (ret & CB) != 0) {
3167                 grub_printf("[BIOS 'Detect Target Operating Mode' "
3168                     "callback unsupported on this platform]\n");
3169                 return (1);     /* unsupported, ignore */
3170         }
3171 
3172         if (ah == 0x0 && (ret & CB) == 0) {
3173                 grub_printf("[BIOS accepted mixed-mode target setting!]\n");
3174                 return (1);     /* told the bios what we're up to */
3175         }
3176 
3177         if (ah == 0 && ret & CB) {
3178                 grub_printf("fatal: BIOS reports this machine CANNOT run in "
3179 		    "mixed 32/64-bit mode!\n");
3180                 return (0);
3181         }
3182 
3183         grub_printf("warning: BIOS Detect Target Operating Mode callback "
3184             "confused.\n         %%ax >> 8 = 0x%x, carry = %d\n", ah,
3185             ret & CB ? 1 : 0);
3186 
3187         return (1);
3188 }
3189 
3190 
3191 int
3192 isamd64()
3193 {
3194 	static int ret = -1;
3195 
3196 	if (ret == -1)
3197 		ret = amd64_config_cpu();
3198 
3199 	return (ret);
3200 }
3201 
3202 static void
3203 expand_arch (char *arg, char *newarg)
3204 {
3205   char *index;
3206 
3207   newarg[0] = '\0';
3208 
3209   while ((index = strstr(arg, "$ISADIR")) != NULL) {
3210 
3211     index[0] = '\0';
3212     strncat(newarg, arg, MAX_CMDLINE);
3213     index[0] = '$';
3214 
3215     if (isamd64() && check_min_mem64())
3216       strncat(newarg, "amd64", MAX_CMDLINE);
3217 
3218     arg = index + 7;
3219   }
3220 
3221   strncat(newarg, arg, MAX_CMDLINE);
3222   return;
3223 }
3224 
3225 /* kernel$ */
3226 static int
3227 kernel_dollar_func (char *arg, int flags)
3228 {
3229   char newarg[MAX_CMDLINE];	/* everything boils down to MAX_CMDLINE */
3230 
3231   grub_printf("loading '%s' ...\n", arg);
3232   expand_arch(arg, newarg);
3233 
3234   if (kernel_func(newarg, flags))
3235 	return (1);
3236 
3237   mb_cmdline = (char *)MB_CMDLINE_BUF;
3238   if (expand_dollar_bootfs(newarg, mb_cmdline)) {
3239 	grub_printf("cannot expand $ZFS-BOOTFS for dataset %s\n",
3240 	    current_bootfs);
3241 	return (1);
3242   }
3243 
3244   grub_printf("'%s' is loaded\n", mb_cmdline);
3245   mb_cmdline += grub_strlen(mb_cmdline) + 1;
3246 
3247   return (0);
3248 }
3249 
3250 static struct builtin builtin_kernel_dollar =
3251 {
3252   "kernel$",
3253   kernel_dollar_func,
3254   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3255   "kernel$ [--no-mem-option] [--type=TYPE] FILE [ARG ...]",
3256   " Just like kernel, but with $ISADIR expansion."
3257 };
3258 
3259 
3260 /* lock */
3261 static int
3262 lock_func (char *arg, int flags)
3263 {
3264   if (! auth && password)
3265     {
3266       errnum = ERR_PRIVILEGED;
3267       return 1;
3268     }
3269 
3270   return 0;
3271 }
3272 
3273 static struct builtin builtin_lock =
3274 {
3275   "lock",
3276   lock_func,
3277   BUILTIN_CMDLINE,
3278   "lock",
3279   "Break a command execution unless the user is authenticated."
3280 };
3281 
3282 
3283 /* makeactive */
3284 static int
3285 makeactive_func (char *arg, int flags)
3286 {
3287   if (! make_saved_active ())
3288     return 1;
3289 
3290   return 0;
3291 }
3292 
3293 static struct builtin builtin_makeactive =
3294 {
3295   "makeactive",
3296   makeactive_func,
3297   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3298   "makeactive",
3299   "Set the active partition on the root disk to GRUB's root device."
3300   " This command is limited to _primary_ PC partitions on a hard disk."
3301 };
3302 
3303 
3304 /* map */
3305 /* Map FROM_DRIVE to TO_DRIVE.  */
3306 static int
3307 map_func (char *arg, int flags)
3308 {
3309   char *to_drive;
3310   char *from_drive;
3311   unsigned long to, from;
3312   int i;
3313 
3314   to_drive = arg;
3315   from_drive = skip_to (0, arg);
3316 
3317   /* Get the drive number for TO_DRIVE.  */
3318   set_device (to_drive);
3319   if (errnum)
3320     return 1;
3321   to = current_drive;
3322 
3323   /* Get the drive number for FROM_DRIVE.  */
3324   set_device (from_drive);
3325   if (errnum)
3326     return 1;
3327   from = current_drive;
3328 
3329   /* Search for an empty slot in BIOS_DRIVE_MAP.  */
3330   for (i = 0; i < DRIVE_MAP_SIZE; i++)
3331     {
3332       /* Perhaps the user wants to override the map.  */
3333       if ((bios_drive_map[i] & 0xff) == from)
3334 	break;
3335 
3336       if (! bios_drive_map[i])
3337 	break;
3338     }
3339 
3340   if (i == DRIVE_MAP_SIZE)
3341     {
3342       errnum = ERR_WONT_FIT;
3343       return 1;
3344     }
3345 
3346   if (to == from)
3347     /* If TO is equal to FROM, delete the entry.  */
3348     grub_memmove ((char *) &bios_drive_map[i], (char *) &bios_drive_map[i + 1],
3349 		  sizeof (unsigned short) * (DRIVE_MAP_SIZE - i));
3350   else
3351     bios_drive_map[i] = from | (to << 8);
3352 
3353   return 0;
3354 }
3355 
3356 static struct builtin builtin_map =
3357 {
3358   "map",
3359   map_func,
3360   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3361   "map TO_DRIVE FROM_DRIVE",
3362   "Map the drive FROM_DRIVE to the drive TO_DRIVE. This is necessary"
3363   " when you chain-load some operating systems, such as DOS, if such an"
3364   " OS resides at a non-first drive."
3365 };
3366 
3367 
3368 #ifdef USE_MD5_PASSWORDS
3369 /* md5crypt */
3370 static int
3371 md5crypt_func (char *arg, int flags)
3372 {
3373   char crypted[36];
3374   char key[32];
3375   unsigned int seed;
3376   int i;
3377   const char *const seedchars =
3378     "./0123456789ABCDEFGHIJKLMNOPQRST"
3379     "UVWXYZabcdefghijklmnopqrstuvwxyz";
3380 
3381   /* First create a salt.  */
3382 
3383   /* The magical prefix.  */
3384   grub_memset (crypted, 0, sizeof (crypted));
3385   grub_memmove (crypted, "$1$", 3);
3386 
3387   /* Create the length of a salt.  */
3388   seed = currticks ();
3389 
3390   /* Generate a salt.  */
3391   for (i = 0; i < 8 && seed; i++)
3392     {
3393       /* FIXME: This should be more random.  */
3394       crypted[3 + i] = seedchars[seed & 0x3f];
3395       seed >>= 6;
3396     }
3397 
3398   /* A salt must be terminated with `$', if it is less than 8 chars.  */
3399   crypted[3 + i] = '$';
3400 
3401 #ifdef DEBUG_MD5CRYPT
3402   grub_printf ("salt = %s\n", crypted);
3403 #endif
3404 
3405   /* Get a password.  */
3406   grub_memset (key, 0, sizeof (key));
3407   get_cmdline ("Password: ", key, sizeof (key) - 1, '*', 0);
3408 
3409   /* Crypt the key.  */
3410   make_md5_password (key, crypted);
3411 
3412   grub_printf ("Encrypted: %s\n", crypted);
3413   return 0;
3414 }
3415 
3416 static struct builtin builtin_md5crypt =
3417 {
3418   "md5crypt",
3419   md5crypt_func,
3420   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3421   "md5crypt",
3422   "Generate a password in MD5 format."
3423 };
3424 #endif /* USE_MD5_PASSWORDS */
3425 
3426 
3427 /* module */
3428 static int
3429 module_func (char *arg, int flags)
3430 {
3431   int len = grub_strlen (arg);
3432 
3433   switch (kernel_type)
3434     {
3435     case KERNEL_TYPE_MULTIBOOT:
3436       if (mb_cmdline + len + 1 > (char *) MB_CMDLINE_BUF + MB_CMDLINE_BUFLEN)
3437 	{
3438 	  errnum = ERR_WONT_FIT;
3439 	  return 1;
3440 	}
3441       grub_memmove (mb_cmdline, arg, len + 1);
3442       if (! load_module (arg, mb_cmdline))
3443 	return 1;
3444 
3445       mb_cmdline += grub_strlen(mb_cmdline) + 1;
3446       break;
3447 
3448     case KERNEL_TYPE_LINUX:
3449     case KERNEL_TYPE_BIG_LINUX:
3450       if (! load_initrd (arg))
3451 	return 1;
3452       break;
3453 
3454     default:
3455       errnum = ERR_NEED_MB_KERNEL;
3456       return 1;
3457     }
3458 
3459   return 0;
3460 }
3461 
3462 static struct builtin builtin_module =
3463 {
3464   "module",
3465   module_func,
3466   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3467   "module FILE [ARG ...]",
3468   "Load a boot module FILE for a Multiboot format boot image (no"
3469   " interpretation of the file contents is made, so users of this"
3470   " command must know what the kernel in question expects). The"
3471   " rest of the line is passed as the \"module command line\", like"
3472   " the `kernel' command."
3473 };
3474 
3475 /* module$ */
3476 static int
3477 module_dollar_func (char *arg, int flags)
3478 {
3479   char newarg[MAX_CMDLINE];	/* everything boils down to MAX_CMDLINE */
3480   char *cmdline_sav;
3481 
3482   grub_printf("loading '%s' ...\n", arg);
3483   expand_arch(arg, newarg);
3484 
3485   cmdline_sav = (char *)mb_cmdline;
3486   if (module_func(newarg, flags))
3487 	return (1);
3488 
3489   if (expand_dollar_bootfs(newarg, cmdline_sav)) {
3490 	grub_printf("cannot expand $ZFS-BOOTFS for dataset %s\n",
3491 	    current_bootfs);
3492 	return (1);
3493   }
3494 
3495   grub_printf("'%s' is loaded\n", (char *)cmdline_sav);
3496   mb_cmdline += grub_strlen(cmdline_sav) + 1;
3497 
3498   return (0);
3499 }
3500 
3501 static struct builtin builtin_module_dollar =
3502 {
3503   "module$",
3504   module_dollar_func,
3505   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3506   "module FILE [ARG ...]",
3507   " Just like module, but with $ISADIR expansion."
3508 };
3509 
3510 
3511 /* modulenounzip */
3512 static int
3513 modulenounzip_func (char *arg, int flags)
3514 {
3515   int ret;
3516 
3517 #ifndef NO_DECOMPRESSION
3518   no_decompression = 1;
3519 #endif
3520 
3521   ret = module_func (arg, flags);
3522 
3523 #ifndef NO_DECOMPRESSION
3524   no_decompression = 0;
3525 #endif
3526 
3527   return ret;
3528 }
3529 
3530 static struct builtin builtin_modulenounzip =
3531 {
3532   "modulenounzip",
3533   modulenounzip_func,
3534   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3535   "modulenounzip FILE [ARG ...]",
3536   "The same as `module', except that automatic decompression is"
3537   " disabled."
3538 };
3539 
3540 
3541 /* pager [on|off] */
3542 static int
3543 pager_func (char *arg, int flags)
3544 {
3545   /* If ARG is empty, toggle the flag.  */
3546   if (! *arg)
3547     use_pager = ! use_pager;
3548   else if (grub_memcmp (arg, "on", 2) == 0)
3549     use_pager = 1;
3550   else if (grub_memcmp (arg, "off", 3) == 0)
3551     use_pager = 0;
3552   else
3553     {
3554       errnum = ERR_BAD_ARGUMENT;
3555       return 1;
3556     }
3557 
3558   grub_printf (" Internal pager is now %s\n", use_pager ? "on" : "off");
3559   return 0;
3560 }
3561 
3562 static struct builtin builtin_pager =
3563 {
3564   "pager",
3565   pager_func,
3566   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
3567   "pager [FLAG]",
3568   "Toggle pager mode with no argument. If FLAG is given and its value"
3569   " is `on', turn on the mode. If FLAG is `off', turn off the mode."
3570 };
3571 
3572 
3573 /* partnew PART TYPE START LEN */
3574 static int
3575 partnew_func (char *arg, int flags)
3576 {
3577   int new_type, new_start, new_len;
3578   int start_cl, start_ch, start_dh;
3579   int end_cl, end_ch, end_dh;
3580   int entry;
3581   char mbr[512];
3582 
3583   /* Convert a LBA address to a CHS address in the INT 13 format.  */
3584   auto void lba_to_chs (int lba, int *cl, int *ch, int *dh);
3585   void lba_to_chs (int lba, int *cl, int *ch, int *dh)
3586     {
3587       int cylinder, head, sector;
3588 
3589       sector = lba % buf_geom.sectors + 1;
3590       head = (lba / buf_geom.sectors) % buf_geom.heads;
3591       cylinder = lba / (buf_geom.sectors * buf_geom.heads);
3592 
3593       if (cylinder >= buf_geom.cylinders)
3594 	cylinder = buf_geom.cylinders - 1;
3595 
3596       *cl = sector | ((cylinder & 0x300) >> 2);
3597       *ch = cylinder & 0xFF;
3598       *dh = head;
3599     }
3600 
3601   /* Get the drive and the partition.  */
3602   if (! set_device (arg))
3603     return 1;
3604 
3605   /* The drive must be a hard disk.  */
3606   if (! (current_drive & 0x80))
3607     {
3608       errnum = ERR_BAD_ARGUMENT;
3609       return 1;
3610     }
3611 
3612   /* The partition must a primary partition.  */
3613   if ((current_partition >> 16) > 3
3614       || (current_partition & 0xFFFF) != 0xFFFF)
3615     {
3616       errnum = ERR_BAD_ARGUMENT;
3617       return 1;
3618     }
3619 
3620   entry = current_partition >> 16;
3621 
3622   /* Get the new partition type.  */
3623   arg = skip_to (0, arg);
3624   if (! safe_parse_maxint (&arg, &new_type))
3625     return 1;
3626 
3627   /* The partition type is unsigned char.  */
3628   if (new_type > 0xFF)
3629     {
3630       errnum = ERR_BAD_ARGUMENT;
3631       return 1;
3632     }
3633 
3634   /* Get the new partition start.  */
3635   arg = skip_to (0, arg);
3636   if (! safe_parse_maxint (&arg, &new_start))
3637     return 1;
3638 
3639   /* Get the new partition length.  */
3640   arg = skip_to (0, arg);
3641   if (! safe_parse_maxint (&arg, &new_len))
3642     return 1;
3643 
3644   /* Read the MBR.  */
3645   if (! rawread (current_drive, 0, 0, SECTOR_SIZE, mbr))
3646     return 1;
3647 
3648   /* Check if the new partition will fit in the disk.  */
3649   if (new_start + new_len > buf_geom.total_sectors)
3650     {
3651       errnum = ERR_GEOM;
3652       return 1;
3653     }
3654 
3655   /* Store the partition information in the MBR.  */
3656   lba_to_chs (new_start, &start_cl, &start_ch, &start_dh);
3657   lba_to_chs (new_start + new_len - 1, &end_cl, &end_ch, &end_dh);
3658 
3659   PC_SLICE_FLAG (mbr, entry) = 0;
3660   PC_SLICE_HEAD (mbr, entry) = start_dh;
3661   PC_SLICE_SEC (mbr, entry) = start_cl;
3662   PC_SLICE_CYL (mbr, entry) = start_ch;
3663   PC_SLICE_TYPE (mbr, entry) = new_type;
3664   PC_SLICE_EHEAD (mbr, entry) = end_dh;
3665   PC_SLICE_ESEC (mbr, entry) = end_cl;
3666   PC_SLICE_ECYL (mbr, entry) = end_ch;
3667   PC_SLICE_START (mbr, entry) = new_start;
3668   PC_SLICE_LENGTH (mbr, entry) = new_len;
3669 
3670   /* Make sure that the MBR has a valid signature.  */
3671   PC_MBR_SIG (mbr) = PC_MBR_SIGNATURE;
3672 
3673   /* Write back the MBR to the disk.  */
3674   buf_track = BUF_CACHE_INVALID;
3675   if (! rawwrite (current_drive, 0, mbr))
3676     return 1;
3677 
3678   return 0;
3679 }
3680 
3681 static struct builtin builtin_partnew =
3682 {
3683   "partnew",
3684   partnew_func,
3685   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
3686   "partnew PART TYPE START LEN",
3687   "Create a primary partition at the starting address START with the"
3688   " length LEN, with the type TYPE. START and LEN are in sector units."
3689 };
3690 
3691 
3692 /* parttype PART TYPE */
3693 static int
3694 parttype_func (char *arg, int flags)
3695 {
3696   int new_type;
3697   unsigned long part = 0xFFFFFF;
3698   unsigned long start, len, offset, ext_offset;
3699   int entry, type;
3700   char mbr[512];
3701 
3702   /* Get the drive and the partition.  */
3703   if (! set_device (arg))
3704     return 1;
3705 
3706   /* The drive must be a hard disk.  */
3707   if (! (current_drive & 0x80))
3708     {
3709       errnum = ERR_BAD_ARGUMENT;
3710       return 1;
3711     }
3712 
3713   /* The partition must be a PC slice.  */
3714   if ((current_partition >> 16) == 0xFF
3715       || (current_partition & 0xFFFF) != 0xFFFF)
3716     {
3717       errnum = ERR_BAD_ARGUMENT;
3718       return 1;
3719     }
3720 
3721   /* Get the new partition type.  */
3722   arg = skip_to (0, arg);
3723   if (! safe_parse_maxint (&arg, &new_type))
3724     return 1;
3725 
3726   /* The partition type is unsigned char.  */
3727   if (new_type > 0xFF)
3728     {
3729       errnum = ERR_BAD_ARGUMENT;
3730       return 1;
3731     }
3732 
3733   /* Look for the partition.  */
3734   while (next_partition (current_drive, 0xFFFFFF, &part, &type,
3735 			 &start, &len, &offset, &entry,
3736 			 &ext_offset, mbr))
3737     {
3738       if (part == current_partition)
3739 	{
3740 	  /* Found.  */
3741 
3742 	  /* Set the type to NEW_TYPE.  */
3743 	  PC_SLICE_TYPE (mbr, entry) = new_type;
3744 
3745 	  /* Write back the MBR to the disk.  */
3746 	  buf_track = BUF_CACHE_INVALID;
3747 	  if (! rawwrite (current_drive, offset, mbr))
3748 	    return 1;
3749 
3750 	  /* Succeed.  */
3751 	  return 0;
3752 	}
3753     }
3754 
3755   /* The partition was not found.  ERRNUM was set by next_partition.  */
3756   return 1;
3757 }
3758 
3759 static struct builtin builtin_parttype =
3760 {
3761   "parttype",
3762   parttype_func,
3763   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
3764   "parttype PART TYPE",
3765   "Change the type of the partition PART to TYPE."
3766 };
3767 
3768 
3769 /* password */
3770 static int
3771 password_func (char *arg, int flags)
3772 {
3773   int len;
3774   password_t type = PASSWORD_PLAIN;
3775 
3776 #ifdef USE_MD5_PASSWORDS
3777   if (grub_memcmp (arg, "--md5", 5) == 0)
3778     {
3779       type = PASSWORD_MD5;
3780       arg = skip_to (0, arg);
3781     }
3782 #endif
3783   if (grub_memcmp (arg, "--", 2) == 0)
3784     {
3785       type = PASSWORD_UNSUPPORTED;
3786       arg = skip_to (0, arg);
3787     }
3788 
3789   if ((flags & (BUILTIN_CMDLINE | BUILTIN_SCRIPT)) != 0)
3790     {
3791       /* Do password check! */
3792       char entered[32];
3793 
3794       /* Wipe out any previously entered password */
3795       entered[0] = 0;
3796       get_cmdline ("Password: ", entered, 31, '*', 0);
3797 
3798       nul_terminate (arg);
3799       if (check_password (entered, arg, type) != 0)
3800 	{
3801 	  errnum = ERR_PRIVILEGED;
3802 	  return 1;
3803 	}
3804     }
3805   else
3806     {
3807       len = grub_strlen (arg);
3808 
3809       /* PASSWORD NUL NUL ... */
3810       if (len + 2 > PASSWORD_BUFLEN)
3811 	{
3812 	  errnum = ERR_WONT_FIT;
3813 	  return 1;
3814 	}
3815 
3816       /* Copy the password and clear the rest of the buffer.  */
3817       password = (char *) PASSWORD_BUF;
3818       grub_memmove (password, arg, len);
3819       grub_memset (password + len, 0, PASSWORD_BUFLEN - len);
3820       password_type = type;
3821     }
3822   return 0;
3823 }
3824 
3825 static struct builtin builtin_password =
3826 {
3827   "password",
3828   password_func,
3829   BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_NO_ECHO,
3830   "password [--md5] PASSWD [FILE]",
3831   "If used in the first section of a menu file, disable all"
3832   " interactive editing control (menu entry editor and"
3833   " command line). If the password PASSWD is entered, it loads the"
3834   " FILE as a new config file and restarts the GRUB Stage 2. If you"
3835   " omit the argument FILE, then GRUB just unlocks privileged"
3836   " instructions.  You can also use it in the script section, in"
3837   " which case it will ask for the password, before continueing."
3838   " The option --md5 tells GRUB that PASSWD is encrypted with"
3839   " md5crypt."
3840 };
3841 
3842 
3843 /* pause */
3844 static int
3845 pause_func (char *arg, int flags)
3846 {
3847   printf("%s\n", arg);
3848 
3849   /* If ESC is returned, then abort this entry.  */
3850   if (ASCII_CHAR (getkey ()) == 27)
3851     return 1;
3852 
3853   return 0;
3854 }
3855 
3856 static struct builtin builtin_pause =
3857 {
3858   "pause",
3859   pause_func,
3860   BUILTIN_CMDLINE | BUILTIN_NO_ECHO,
3861   "pause [MESSAGE ...]",
3862   "Print MESSAGE, then wait until a key is pressed."
3863 };
3864 
3865 
3866 #ifdef GRUB_UTIL
3867 /* quit */
3868 static int
3869 quit_func (char *arg, int flags)
3870 {
3871   stop ();
3872 
3873   /* Never reach here.  */
3874   return 0;
3875 }
3876 
3877 static struct builtin builtin_quit =
3878 {
3879   "quit",
3880   quit_func,
3881   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3882   "quit",
3883   "Exit from the GRUB shell."
3884 };
3885 #endif /* GRUB_UTIL */
3886 
3887 
3888 #ifdef SUPPORT_NETBOOT
3889 /* rarp */
3890 static int
3891 rarp_func (char *arg, int flags)
3892 {
3893   if (! rarp ())
3894     {
3895       if (errnum == ERR_NONE)
3896 	errnum = ERR_DEV_VALUES;
3897 
3898       return 1;
3899     }
3900 
3901   /* Notify the configuration.  */
3902   print_network_configuration ();
3903   return 0;
3904 }
3905 
3906 static struct builtin builtin_rarp =
3907 {
3908   "rarp",
3909   rarp_func,
3910   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
3911   "rarp",
3912   "Initialize a network device via RARP."
3913 };
3914 #endif /* SUPPORT_NETBOOT */
3915 
3916 
3917 static int
3918 read_func (char *arg, int flags)
3919 {
3920   int addr;
3921 
3922   if (! safe_parse_maxint (&arg, &addr))
3923     return 1;
3924 
3925   grub_printf ("Address 0x%x: Value 0x%x\n",
3926 	       addr, *((unsigned *) RAW_ADDR (addr)));
3927   return 0;
3928 }
3929 
3930 static struct builtin builtin_read =
3931 {
3932   "read",
3933   read_func,
3934   BUILTIN_CMDLINE,
3935   "read ADDR",
3936   "Read a 32-bit value from memory at address ADDR and"
3937   " display it in hex format."
3938 };
3939 
3940 
3941 /* reboot */
3942 static int
3943 reboot_func (char *arg, int flags)
3944 {
3945   grub_reboot ();
3946 
3947   /* Never reach here.  */
3948   return 1;
3949 }
3950 
3951 static struct builtin builtin_reboot =
3952 {
3953   "reboot",
3954   reboot_func,
3955   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
3956   "reboot",
3957   "Reboot your system."
3958 };
3959 
3960 
3961 /* Print the root device information.  */
3962 static void
3963 print_root_device (void)
3964 {
3965   if (saved_drive == NETWORK_DRIVE)
3966     {
3967       /* Network drive.  */
3968       grub_printf (" (nd):");
3969     }
3970   else if (saved_drive & 0x80)
3971     {
3972       /* Hard disk drive.  */
3973       grub_printf (" (hd%d", saved_drive - 0x80);
3974 
3975       if ((saved_partition & 0xFF0000) != 0xFF0000)
3976 	grub_printf (",%d", saved_partition >> 16);
3977 
3978       if ((saved_partition & 0x00FF00) != 0x00FF00)
3979 	grub_printf (",%c", ((saved_partition >> 8) & 0xFF) + 'a');
3980 
3981       grub_printf ("):");
3982     }
3983   else
3984     {
3985       /* Floppy disk drive.  */
3986       grub_printf (" (fd%d):", saved_drive);
3987     }
3988 
3989   /* Print the filesystem information.  */
3990   current_partition = saved_partition;
3991   current_drive = saved_drive;
3992   print_fsys_type ();
3993 }
3994 
3995 static int
3996 real_root_func (char *arg, int attempt_mount)
3997 {
3998   int hdbias = 0;
3999   char *biasptr;
4000   char *next;
4001 
4002   /* If ARG is empty, just print the current root device.  */
4003   if (! *arg)
4004     {
4005       print_root_device ();
4006       return 0;
4007     }
4008 
4009   /* Call set_device to get the drive and the partition in ARG.  */
4010   next = set_device (arg);
4011   if (! next)
4012     return 1;
4013 
4014   /* Ignore ERR_FSYS_MOUNT.  */
4015   if (attempt_mount)
4016     {
4017       if (! open_device () && errnum != ERR_FSYS_MOUNT)
4018 	return 1;
4019     }
4020   else
4021     {
4022       /* This is necessary, because the location of a partition table
4023 	 must be set appropriately.  */
4024       if (open_partition ())
4025 	{
4026 	  set_bootdev (0);
4027 	  if (errnum)
4028 	    return 1;
4029 	}
4030     }
4031 
4032   /* Clear ERRNUM.  */
4033   errnum = 0;
4034   saved_partition = current_partition;
4035   saved_drive = current_drive;
4036 
4037   if (attempt_mount)
4038     {
4039       /* BSD and chainloading evil hacks !!  */
4040       biasptr = skip_to (0, next);
4041       safe_parse_maxint (&biasptr, &hdbias);
4042       errnum = 0;
4043       bootdev = set_bootdev (hdbias);
4044       if (errnum)
4045 	return 1;
4046 
4047       /* Print the type of the filesystem.  */
4048       print_fsys_type ();
4049     }
4050 
4051   return 0;
4052 }
4053 
4054 static int
4055 root_func (char *arg, int flags)
4056 {
4057   is_zfs_mount = 0;
4058   return real_root_func (arg, 1);
4059 }
4060 
4061 static struct builtin builtin_root =
4062 {
4063   "root",
4064   root_func,
4065   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
4066   "root [DEVICE [HDBIAS]]",
4067   "Set the current \"root device\" to the device DEVICE, then"
4068   " attempt to mount it to get the partition size (for passing the"
4069   " partition descriptor in `ES:ESI', used by some chain-loaded"
4070   " bootloaders), the BSD drive-type (for booting BSD kernels using"
4071   " their native boot format), and correctly determine "
4072   " the PC partition where a BSD sub-partition is located. The"
4073   " optional HDBIAS parameter is a number to tell a BSD kernel"
4074   " how many BIOS drive numbers are on controllers before the current"
4075   " one. For example, if there is an IDE disk and a SCSI disk, and your"
4076   " FreeBSD root partition is on the SCSI disk, then use a `1' for HDBIAS."
4077 };
4078 
4079 
4080 /* findroot */
4081 static int
4082 findroot_func (char *arg, int flags)
4083 {
4084   int ret;
4085   char root[32];
4086 
4087   if (grub_strlen(arg) >= BOOTSIGN_ARGLEN) {
4088   	errnum = ERR_BAD_ARGUMENT;
4089 	return 1;
4090   }
4091 
4092   if (arg[0] == '\0') {
4093   	errnum = ERR_BAD_ARGUMENT;
4094 	return 1;
4095   }
4096 
4097   if (grub_strchr(arg, '/')) {
4098   	errnum = ERR_BAD_ARGUMENT;
4099 	return 1;
4100   }
4101 
4102   find_best_root = 1;
4103   best_drive = 0;
4104   best_part = 0;
4105   ret = find_common(arg, root, 1, flags);
4106   if (ret != 0)
4107 	return (ret);
4108   find_best_root = 0;
4109 
4110   return real_root_func (root, 1);
4111 }
4112 
4113 static struct builtin builtin_findroot =
4114 {
4115   "findroot",
4116   findroot_func,
4117   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
4118   "findroot  <SIGNATURE | (SIGNATURE,partition[,slice])>",
4119   "Searches across all partitions for the file name SIGNATURE."
4120   " GRUB looks only in the directory /boot/grub/bootsign for the"
4121   " filename and it stops as soon as it finds the first instance of"
4122   " the file - so to be useful the name of the signature file must be"
4123   " unique across all partitions. Once the signature file is found,"
4124   " GRUB invokes the \"root\" command on that partition."
4125   " An optional partition and slice may be specified to optimize the search."
4126 };
4127 
4128 
4129 /*
4130  * COMMAND to override the default root filesystem for ZFS
4131  *	bootfs pool/fs
4132  */
4133 static int
4134 bootfs_func (char *arg, int flags)
4135 {
4136 	int hdbias = 0;
4137 	char *biasptr;
4138 	char *next;
4139 
4140 	if (! *arg) {
4141 	    if (current_bootfs[0] != '\0')
4142 		grub_printf ("The zfs boot filesystem is set to '%s'.\n",
4143 				current_bootfs);
4144 	    else if (current_rootpool[0] != 0 && current_bootfs_obj != 0)
4145 		grub_printf("The zfs boot filesystem is <default: %s/%u>.",
4146 				current_rootpool, current_bootfs_obj);
4147 	    else
4148 		grub_printf ("The zfs boot filesystem will be derived from "
4149 			"the default bootfs pool property.\n");
4150 
4151 	    return (1);
4152 	}
4153 
4154 	/* Verify the zfs filesystem name */
4155 	if (arg[0] == '/' || arg[0] == '\0') {
4156 		errnum = ERR_BAD_ARGUMENT;
4157 		return 0;
4158 	}
4159 	if (current_rootpool[0] != 0 && grub_strncmp(arg,
4160 	    current_rootpool, strlen(current_rootpool))) {
4161 		errnum = ERR_BAD_ARGUMENT;
4162 		return 0;
4163 	}
4164 
4165 	if (set_bootfs(arg) == 0) {
4166 		errnum = ERR_BAD_ARGUMENT;
4167 		return 0;
4168 	}
4169 
4170 	return (1);
4171 }
4172 
4173 static struct builtin builtin_bootfs =
4174 {
4175   "bootfs",
4176   bootfs_func,
4177   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
4178   "bootfs [ZFSBOOTFS]",
4179   "Set the current zfs boot filesystem to ZFSBOOTFS (rootpool/rootfs)."
4180 };
4181 
4182 
4183 /* rootnoverify */
4184 static int
4185 rootnoverify_func (char *arg, int flags)
4186 {
4187   return real_root_func (arg, 0);
4188 }
4189 
4190 static struct builtin builtin_rootnoverify =
4191 {
4192   "rootnoverify",
4193   rootnoverify_func,
4194   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
4195   "rootnoverify [DEVICE [HDBIAS]]",
4196   "Similar to `root', but don't attempt to mount the partition. This"
4197   " is useful for when an OS is outside of the area of the disk that"
4198   " GRUB can read, but setting the correct root device is still"
4199   " desired. Note that the items mentioned in `root' which"
4200   " derived from attempting the mount will NOT work correctly."
4201 };
4202 
4203 
4204 /* savedefault */
4205 static int
4206 savedefault_func (char *arg, int flags)
4207 {
4208 #if !defined(SUPPORT_DISKLESS) && !defined(GRUB_UTIL)
4209   unsigned long tmp_drive = saved_drive;
4210   unsigned long tmp_partition = saved_partition;
4211   char *default_file = (char *) DEFAULT_FILE_BUF;
4212   char buf[10];
4213   char sect[SECTOR_SIZE];
4214   int entryno;
4215   int sector_count = 0;
4216   unsigned int saved_sectors[2];
4217   int saved_offsets[2];
4218   int saved_lengths[2];
4219 
4220   /* not supported for zfs root */
4221   if (is_zfs_mount == 1) {
4222 	return (0); /* no-op */
4223   }
4224 
4225   /* Save sector information about at most two sectors.  */
4226   auto void disk_read_savesect_func (unsigned int sector, int offset,
4227       int length);
4228   void disk_read_savesect_func (unsigned int sector, int offset, int length)
4229     {
4230       if (sector_count < 2)
4231 	{
4232 	  saved_sectors[sector_count] = sector;
4233 	  saved_offsets[sector_count] = offset;
4234 	  saved_lengths[sector_count] = length;
4235 	}
4236       sector_count++;
4237     }
4238 
4239   /* This command is only useful when you boot an entry from the menu
4240      interface.  */
4241   if (! (flags & BUILTIN_SCRIPT))
4242     {
4243       errnum = ERR_UNRECOGNIZED;
4244       return 1;
4245     }
4246 
4247   /* Determine a saved entry number.  */
4248   if (*arg)
4249     {
4250       if (grub_memcmp (arg, "fallback", sizeof ("fallback") - 1) == 0)
4251 	{
4252 	  int i;
4253 	  int index = 0;
4254 
4255 	  for (i = 0; i < MAX_FALLBACK_ENTRIES; i++)
4256 	    {
4257 	      if (fallback_entries[i] < 0)
4258 		break;
4259 	      if (fallback_entries[i] == current_entryno)
4260 		{
4261 		  index = i + 1;
4262 		  break;
4263 		}
4264 	    }
4265 
4266 	  if (index >= MAX_FALLBACK_ENTRIES || fallback_entries[index] < 0)
4267 	    {
4268 	      /* This is the last.  */
4269 	      errnum = ERR_BAD_ARGUMENT;
4270 	      return 1;
4271 	    }
4272 
4273 	  entryno = fallback_entries[index];
4274 	}
4275       else if (! safe_parse_maxint (&arg, &entryno))
4276 	return 1;
4277     }
4278   else
4279     entryno = current_entryno;
4280 
4281   /* Open the default file.  */
4282   saved_drive = boot_drive;
4283   saved_partition = install_partition;
4284   if (grub_open (default_file))
4285     {
4286       int len;
4287 
4288       disk_read_hook = disk_read_savesect_func;
4289       len = grub_read (buf, sizeof (buf));
4290       disk_read_hook = 0;
4291       grub_close ();
4292 
4293       if (len != sizeof (buf))
4294 	{
4295 	  /* This is too small. Do not modify the file manually, please!  */
4296 	  errnum = ERR_READ;
4297 	  goto fail;
4298 	}
4299 
4300       if (sector_count > 2)
4301 	{
4302 	  /* Is this possible?! Too fragmented!  */
4303 	  errnum = ERR_FSYS_CORRUPT;
4304 	  goto fail;
4305 	}
4306 
4307       /* Set up a string to be written.  */
4308       grub_memset (buf, '\n', sizeof (buf));
4309       grub_sprintf (buf, "%d", entryno);
4310 
4311       if (saved_lengths[0] < sizeof (buf))
4312 	{
4313 	  /* The file is anchored to another file and the first few bytes
4314 	     are spanned in two sectors. Uggh...  */
4315 	  if (! rawread (current_drive, saved_sectors[0], 0, SECTOR_SIZE,
4316 			 sect))
4317 	    goto fail;
4318 	  grub_memmove (sect + saved_offsets[0], buf, saved_lengths[0]);
4319 	  if (! rawwrite (current_drive, saved_sectors[0], sect))
4320 	    goto fail;
4321 
4322 	  if (! rawread (current_drive, saved_sectors[1], 0, SECTOR_SIZE,
4323 			 sect))
4324 	    goto fail;
4325 	  grub_memmove (sect + saved_offsets[1],
4326 			buf + saved_lengths[0],
4327 			sizeof (buf) - saved_lengths[0]);
4328 	  if (! rawwrite (current_drive, saved_sectors[1], sect))
4329 	    goto fail;
4330 	}
4331       else
4332 	{
4333 	  /* This is a simple case. It fits into a single sector.  */
4334 	  if (! rawread (current_drive, saved_sectors[0], 0, SECTOR_SIZE,
4335 			 sect))
4336 	    goto fail;
4337 	  grub_memmove (sect + saved_offsets[0], buf, sizeof (buf));
4338 	  if (! rawwrite (current_drive, saved_sectors[0], sect))
4339 	    goto fail;
4340 	}
4341 
4342       /* Clear the cache.  */
4343       buf_track = BUF_CACHE_INVALID;
4344     }
4345 
4346  fail:
4347   saved_drive = tmp_drive;
4348   saved_partition = tmp_partition;
4349   return errnum;
4350 #else /* ! SUPPORT_DISKLESS && ! GRUB_UTIL */
4351   errnum = ERR_UNRECOGNIZED;
4352   return 1;
4353 #endif /* ! SUPPORT_DISKLESS && ! GRUB_UTIL */
4354 }
4355 
4356 static struct builtin builtin_savedefault =
4357 {
4358   "savedefault",
4359   savedefault_func,
4360   BUILTIN_CMDLINE,
4361   "savedefault [NUM | `fallback']",
4362   "Save the current entry as the default boot entry if no argument is"
4363   " specified. If a number is specified, this number is saved. If"
4364   " `fallback' is used, next fallback entry is saved."
4365 };
4366 
4367 
4368 #ifdef SUPPORT_SERIAL
4369 /* serial */
4370 static int
4371 serial_func (char *arg, int flags)
4372 {
4373   unsigned short port = serial_hw_get_port (0);
4374   unsigned int speed = 9600;
4375   int word_len = UART_8BITS_WORD;
4376   int parity = UART_NO_PARITY;
4377   int stop_bit_len = UART_1_STOP_BIT;
4378 
4379   /* Process GNU-style long options.
4380      FIXME: We should implement a getopt-like function, to avoid
4381      duplications.  */
4382   while (1)
4383     {
4384       if (grub_memcmp (arg, "--unit=", sizeof ("--unit=") - 1) == 0)
4385 	{
4386 	  char *p = arg + sizeof ("--unit=") - 1;
4387 	  int unit;
4388 
4389 	  if (! safe_parse_maxint (&p, &unit))
4390 	    return 1;
4391 
4392 	  if (unit < 0 || unit > 3)
4393 	    {
4394 	      errnum = ERR_DEV_VALUES;
4395 	      return 1;
4396 	    }
4397 
4398 	  port = serial_hw_get_port (unit);
4399 	}
4400       else if (grub_memcmp (arg, "--speed=", sizeof ("--speed=") - 1) == 0)
4401 	{
4402 	  char *p = arg + sizeof ("--speed=") - 1;
4403 	  int num;
4404 
4405 	  if (! safe_parse_maxint (&p, &num))
4406 	    return 1;
4407 
4408 	  speed = (unsigned int) num;
4409 	}
4410       else if (grub_memcmp (arg, "--port=", sizeof ("--port=") - 1) == 0)
4411 	{
4412 	  char *p = arg + sizeof ("--port=") - 1;
4413 	  int num;
4414 
4415 	  if (! safe_parse_maxint (&p, &num))
4416 	    return 1;
4417 
4418 	  port = (unsigned short) num;
4419 	}
4420       else if (grub_memcmp (arg, "--word=", sizeof ("--word=") - 1) == 0)
4421 	{
4422 	  char *p = arg + sizeof ("--word=") - 1;
4423 	  int len;
4424 
4425 	  if (! safe_parse_maxint (&p, &len))
4426 	    return 1;
4427 
4428 	  switch (len)
4429 	    {
4430 	    case 5: word_len = UART_5BITS_WORD; break;
4431 	    case 6: word_len = UART_6BITS_WORD; break;
4432 	    case 7: word_len = UART_7BITS_WORD; break;
4433 	    case 8: word_len = UART_8BITS_WORD; break;
4434 	    default:
4435 	      errnum = ERR_BAD_ARGUMENT;
4436 	      return 1;
4437 	    }
4438 	}
4439       else if (grub_memcmp (arg, "--stop=", sizeof ("--stop=") - 1) == 0)
4440 	{
4441 	  char *p = arg + sizeof ("--stop=") - 1;
4442 	  int len;
4443 
4444 	  if (! safe_parse_maxint (&p, &len))
4445 	    return 1;
4446 
4447 	  switch (len)
4448 	    {
4449 	    case 1: stop_bit_len = UART_1_STOP_BIT; break;
4450 	    case 2: stop_bit_len = UART_2_STOP_BITS; break;
4451 	    default:
4452 	      errnum = ERR_BAD_ARGUMENT;
4453 	      return 1;
4454 	    }
4455 	}
4456       else if (grub_memcmp (arg, "--parity=", sizeof ("--parity=") - 1) == 0)
4457 	{
4458 	  char *p = arg + sizeof ("--parity=") - 1;
4459 
4460 	  if (grub_memcmp (p, "no", sizeof ("no") - 1) == 0)
4461 	    parity = UART_NO_PARITY;
4462 	  else if (grub_memcmp (p, "odd", sizeof ("odd") - 1) == 0)
4463 	    parity = UART_ODD_PARITY;
4464 	  else if (grub_memcmp (p, "even", sizeof ("even") - 1) == 0)
4465 	    parity = UART_EVEN_PARITY;
4466 	  else
4467 	    {
4468 	      errnum = ERR_BAD_ARGUMENT;
4469 	      return 1;
4470 	    }
4471 	}
4472 # ifdef GRUB_UTIL
4473       /* In the grub shell, don't use any port number but open a tty
4474 	 device instead.  */
4475       else if (grub_memcmp (arg, "--device=", sizeof ("--device=") - 1) == 0)
4476 	{
4477 	  char *p = arg + sizeof ("--device=") - 1;
4478 	  char dev[256];	/* XXX */
4479 	  char *q = dev;
4480 
4481 	  while (*p && ! grub_isspace (*p))
4482 	    *q++ = *p++;
4483 
4484 	  *q = 0;
4485 	  serial_set_device (dev);
4486 	}
4487 # endif /* GRUB_UTIL */
4488       else
4489 	break;
4490 
4491       arg = skip_to (0, arg);
4492     }
4493 
4494   /* Initialize the serial unit.  */
4495   if (! serial_hw_init (port, speed, word_len, parity, stop_bit_len))
4496     {
4497       errnum = ERR_BAD_ARGUMENT;
4498       return 1;
4499     }
4500 
4501   return 0;
4502 }
4503 
4504 static struct builtin builtin_serial =
4505 {
4506   "serial",
4507   serial_func,
4508   BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
4509   "serial [--unit=UNIT] [--port=PORT] [--speed=SPEED] [--word=WORD] [--parity=PARITY] [--stop=STOP] [--device=DEV]",
4510   "Initialize a serial device. UNIT is a digit that specifies which serial"
4511   " device is used (e.g. 0 == COM1). If you need to specify the port number,"
4512   " set it by --port. SPEED is the DTE-DTE speed. WORD is the word length,"
4513   " PARITY is the type of parity, which is one of `no', `odd' and `even'."
4514   " STOP is the length of stop bit(s). The option --device can be used only"
4515   " in the grub shell, which specifies the file name of a tty device. The"
4516   " default values are COM1, 9600, 8N1."
4517 };
4518 #endif /* SUPPORT_SERIAL */
4519 
4520 
4521 /* setkey */
4522 struct keysym
4523 {
4524   char *unshifted_name;			/* the name in unshifted state */
4525   char *shifted_name;			/* the name in shifted state */
4526   unsigned char unshifted_ascii;	/* the ascii code in unshifted state */
4527   unsigned char shifted_ascii;		/* the ascii code in shifted state */
4528   unsigned char keycode;		/* keyboard scancode */
4529 };
4530 
4531 /* The table for key symbols. If the "shifted" member of an entry is
4532    NULL, the entry does not have shifted state.  */
4533 static struct keysym keysym_table[] =
4534 {
4535   {"escape",		0,		0x1b,	0,	0x01},
4536   {"1",			"exclam",	'1',	'!',	0x02},
4537   {"2",			"at",		'2',	'@',	0x03},
4538   {"3",			"numbersign",	'3',	'#',	0x04},
4539   {"4",			"dollar",	'4',	'$',	0x05},
4540   {"5",			"percent",	'5',	'%',	0x06},
4541   {"6",			"caret",	'6',	'^',	0x07},
4542   {"7",			"ampersand",	'7',	'&',	0x08},
4543   {"8",			"asterisk",	'8',	'*',	0x09},
4544   {"9",			"parenleft",	'9',	'(',	0x0a},
4545   {"0",			"parenright",	'0',	')',	0x0b},
4546   {"minus",		"underscore",	'-',	'_',	0x0c},
4547   {"equal",		"plus",		'=',	'+',	0x0d},
4548   {"backspace",		0,		'\b',	0,	0x0e},
4549   {"tab",		0,		'\t',	0,	0x0f},
4550   {"q",			"Q",		'q',	'Q',	0x10},
4551   {"w",			"W",		'w',	'W',	0x11},
4552   {"e",			"E",		'e',	'E',	0x12},
4553   {"r",			"R",		'r',	'R',	0x13},
4554   {"t",			"T",		't',	'T',	0x14},
4555   {"y",			"Y",		'y',	'Y',	0x15},
4556   {"u",			"U",		'u',	'U',	0x16},
4557   {"i",			"I",		'i',	'I',	0x17},
4558   {"o",			"O",		'o',	'O',	0x18},
4559   {"p",			"P",		'p',	'P',	0x19},
4560   {"bracketleft",	"braceleft",	'[',	'{',	0x1a},
4561   {"bracketright",	"braceright",	']',	'}',	0x1b},
4562   {"enter",		0,		'\n',	0,	0x1c},
4563   {"control",		0,		0,	0,	0x1d},
4564   {"a",			"A",		'a',	'A',	0x1e},
4565   {"s",			"S",		's',	'S',	0x1f},
4566   {"d",			"D",		'd',	'D',	0x20},
4567   {"f",			"F",		'f',	'F',	0x21},
4568   {"g",			"G",		'g',	'G',	0x22},
4569   {"h",			"H",		'h',	'H',	0x23},
4570   {"j",			"J",		'j',	'J',	0x24},
4571   {"k",			"K",		'k',	'K',	0x25},
4572   {"l",			"L",		'l',	'L',	0x26},
4573   {"semicolon",		"colon",	';',	':',	0x27},
4574   {"quote",		"doublequote",	'\'',	'"',	0x28},
4575   {"backquote",		"tilde",	'`',	'~',	0x29},
4576   {"shift",		0,		0,	0,	0x2a},
4577   {"backslash",		"bar",		'\\',	'|',	0x2b},
4578   {"z",			"Z",		'z',	'Z',	0x2c},
4579   {"x",			"X",		'x',	'X',	0x2d},
4580   {"c",			"C",		'c',	'C',	0x2e},
4581   {"v",			"V",		'v',	'V',	0x2f},
4582   {"b",			"B",		'b',	'B',	0x30},
4583   {"n",			"N",		'n',	'N',	0x31},
4584   {"m",			"M",		'm',	'M',	0x32},
4585   {"comma",		"less",		',',	'<',	0x33},
4586   {"period",		"greater",	'.',	'>',	0x34},
4587   {"slash",		"question",	'/',	'?',	0x35},
4588   {"alt",		0,		0,	0,	0x38},
4589   {"space",		0,		' ',	0,	0x39},
4590   {"capslock",		0,		0,	0,	0x3a},
4591   {"F1",		0,		0,	0,	0x3b},
4592   {"F2",		0,		0,	0,	0x3c},
4593   {"F3",		0,		0,	0,	0x3d},
4594   {"F4",		0,		0,	0,	0x3e},
4595   {"F5",		0,		0,	0,	0x3f},
4596   {"F6",		0,		0,	0,	0x40},
4597   {"F7",		0,		0,	0,	0x41},
4598   {"F8",		0,		0,	0,	0x42},
4599   {"F9",		0,		0,	0,	0x43},
4600   {"F10",		0,		0,	0,	0x44},
4601   /* Caution: do not add NumLock here! we cannot deal with it properly.  */
4602   {"delete",		0,		0x7f,	0,	0x53}
4603 };
4604 
4605 static int
4606 setkey_func (char *arg, int flags)
4607 {
4608   char *to_key, *from_key;
4609   int to_code, from_code;
4610   int map_in_interrupt = 0;
4611 
4612   auto int find_key_code (char *key);
4613   auto int find_ascii_code (char *key);
4614 
4615   auto int find_key_code (char *key)
4616     {
4617       int i;
4618 
4619       for (i = 0; i < sizeof (keysym_table) / sizeof (keysym_table[0]); i++)
4620 	{
4621 	  if (keysym_table[i].unshifted_name &&
4622 	      grub_strcmp (key, keysym_table[i].unshifted_name) == 0)
4623 	    return keysym_table[i].keycode;
4624 	  else if (keysym_table[i].shifted_name &&
4625 		   grub_strcmp (key, keysym_table[i].shifted_name) == 0)
4626 	    return keysym_table[i].keycode;
4627 	}
4628 
4629       return 0;
4630     }
4631 
4632   auto int find_ascii_code (char *key)
4633     {
4634       int i;
4635 
4636       for (i = 0; i < sizeof (keysym_table) / sizeof (keysym_table[0]); i++)
4637 	{
4638 	  if (keysym_table[i].unshifted_name &&
4639 	      grub_strcmp (key, keysym_table[i].unshifted_name) == 0)
4640 	    return keysym_table[i].unshifted_ascii;
4641 	  else if (keysym_table[i].shifted_name &&
4642 		   grub_strcmp (key, keysym_table[i].shifted_name) == 0)
4643 	    return keysym_table[i].shifted_ascii;
4644 	}
4645 
4646       return 0;
4647     }
4648 
4649   to_key = arg;
4650   from_key = skip_to (0, to_key);
4651 
4652   if (! *to_key)
4653     {
4654       /* If the user specifies no argument, reset the key mappings.  */
4655       grub_memset (bios_key_map, 0, KEY_MAP_SIZE * sizeof (unsigned short));
4656       grub_memset (ascii_key_map, 0, KEY_MAP_SIZE * sizeof (unsigned short));
4657 
4658       return 0;
4659     }
4660   else if (! *from_key)
4661     {
4662       /* The user must specify two arguments or zero argument.  */
4663       errnum = ERR_BAD_ARGUMENT;
4664       return 1;
4665     }
4666 
4667   nul_terminate (to_key);
4668   nul_terminate (from_key);
4669 
4670   to_code = find_ascii_code (to_key);
4671   from_code = find_ascii_code (from_key);
4672   if (! to_code || ! from_code)
4673     {
4674       map_in_interrupt = 1;
4675       to_code = find_key_code (to_key);
4676       from_code = find_key_code (from_key);
4677       if (! to_code || ! from_code)
4678 	{
4679 	  errnum = ERR_BAD_ARGUMENT;
4680 	  return 1;
4681 	}
4682     }
4683 
4684   if (map_in_interrupt)
4685     {
4686       int i;
4687 
4688       /* Find an empty slot.  */
4689       for (i = 0; i < KEY_MAP_SIZE; i++)
4690 	{
4691 	  if ((bios_key_map[i] & 0xff) == from_code)
4692 	    /* Perhaps the user wants to overwrite the map.  */
4693 	    break;
4694 
4695 	  if (! bios_key_map[i])
4696 	    break;
4697 	}
4698 
4699       if (i == KEY_MAP_SIZE)
4700 	{
4701 	  errnum = ERR_WONT_FIT;
4702 	  return 1;
4703 	}
4704 
4705       if (to_code == from_code)
4706 	/* If TO is equal to FROM, delete the entry.  */
4707 	grub_memmove ((char *) &bios_key_map[i],
4708 		      (char *) &bios_key_map[i + 1],
4709 		      sizeof (unsigned short) * (KEY_MAP_SIZE - i));
4710       else
4711 	bios_key_map[i] = (to_code << 8) | from_code;
4712 
4713       /* Ugly but should work.  */
4714       unset_int15_handler ();
4715       set_int15_handler ();
4716     }
4717   else
4718     {
4719       int i;
4720 
4721       /* Find an empty slot.  */
4722       for (i = 0; i < KEY_MAP_SIZE; i++)
4723 	{
4724 	  if ((ascii_key_map[i] & 0xff) == from_code)
4725 	    /* Perhaps the user wants to overwrite the map.  */
4726 	    break;
4727 
4728 	  if (! ascii_key_map[i])
4729 	    break;
4730 	}
4731 
4732       if (i == KEY_MAP_SIZE)
4733 	{
4734 	  errnum = ERR_WONT_FIT;
4735 	  return 1;
4736 	}
4737 
4738       if (to_code == from_code)
4739 	/* If TO is equal to FROM, delete the entry.  */
4740 	grub_memmove ((char *) &ascii_key_map[i],
4741 		      (char *) &ascii_key_map[i + 1],
4742 		      sizeof (unsigned short) * (KEY_MAP_SIZE - i));
4743       else
4744 	ascii_key_map[i] = (to_code << 8) | from_code;
4745     }
4746 
4747   return 0;
4748 }
4749 
4750 static struct builtin builtin_setkey =
4751 {
4752   "setkey",
4753   setkey_func,
4754   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
4755   "setkey [TO_KEY FROM_KEY]",
4756   "Change the keyboard map. The key FROM_KEY is mapped to the key TO_KEY."
4757   " A key must be an alphabet, a digit, or one of these: escape, exclam,"
4758   " at, numbersign, dollar, percent, caret, ampersand, asterisk, parenleft,"
4759   " parenright, minus, underscore, equal, plus, backspace, tab, bracketleft,"
4760   " braceleft, bracketright, braceright, enter, control, semicolon, colon,"
4761   " quote, doublequote, backquote, tilde, shift, backslash, bar, comma,"
4762   " less, period, greater, slash, question, alt, space, capslock, FX (X"
4763   " is a digit), and delete. If no argument is specified, reset key"
4764   " mappings."
4765 };
4766 
4767 
4768 /* setup */
4769 static int
4770 setup_func (char *arg, int flags)
4771 {
4772   /* Point to the string of the installed drive/partition.  */
4773   char *install_ptr;
4774   /* Point to the string of the drive/parition where the GRUB images
4775      reside.  */
4776   char *image_ptr;
4777   unsigned long installed_drive, installed_partition;
4778   unsigned long image_drive, image_partition;
4779   unsigned long tmp_drive, tmp_partition;
4780   char stage1[64];
4781   char stage2[64];
4782   char config_filename[64];
4783   char real_config_filename[64];
4784   char cmd_arg[256];
4785   char device[16];
4786   char *buffer = (char *) RAW_ADDR (0x100000);
4787   int is_force_lba = 0;
4788   char *stage2_arg = 0;
4789   char *prefix = 0;
4790 
4791   auto int check_file (char *file);
4792   auto void sprint_device (int drive, int partition);
4793   auto int embed_stage1_5 (char * stage1_5, int drive, int partition);
4794 
4795   /* Check if the file FILE exists like Autoconf.  */
4796   int check_file (char *file)
4797     {
4798       int ret;
4799 
4800       grub_printf (" Checking if \"%s\" exists... ", file);
4801       ret = grub_open (file);
4802       if (ret)
4803 	{
4804 	  grub_close ();
4805 	  grub_printf ("yes\n");
4806 	}
4807       else
4808 	grub_printf ("no\n");
4809 
4810       return ret;
4811     }
4812 
4813   /* Construct a device name in DEVICE.  */
4814   void sprint_device (int drive, int partition)
4815     {
4816       grub_sprintf (device, "(%cd%d",
4817 		    (drive & 0x80) ? 'h' : 'f',
4818 		    drive & ~0x80);
4819       if ((partition & 0xFF0000) != 0xFF0000)
4820 	{
4821 	  char tmp[16];
4822 	  grub_sprintf (tmp, ",%d", (partition >> 16) & 0xFF);
4823 	  grub_strncat (device, tmp, 256);
4824 	}
4825       if ((partition & 0x00FF00) != 0x00FF00)
4826 	{
4827 	  char tmp[16];
4828 	  grub_sprintf (tmp, ",%c", 'a' + ((partition >> 8) & 0xFF));
4829 	  grub_strncat (device, tmp, 256);
4830 	}
4831       grub_strncat (device, ")", 256);
4832     }
4833 
4834   int embed_stage1_5 (char *stage1_5, int drive, int partition)
4835     {
4836       /* We install GRUB into the MBR, so try to embed the
4837 	 Stage 1.5 in the sectors right after the MBR.  */
4838       sprint_device (drive, partition);
4839       grub_sprintf (cmd_arg, "%s %s", stage1_5, device);
4840 
4841       /* Notify what will be run.  */
4842       grub_printf (" Running \"embed %s\"... ", cmd_arg);
4843 
4844       embed_func (cmd_arg, flags);
4845       if (! errnum)
4846 	{
4847 	  /* Construct the blocklist representation.  */
4848 	  grub_sprintf (buffer, "%s%s", device, embed_info);
4849 	  grub_printf ("succeeded\n");
4850 	  return 1;
4851 	}
4852       else
4853 	{
4854 	  grub_printf ("failed (this is not fatal)\n");
4855 	  return 0;
4856 	}
4857     }
4858 
4859   struct stage1_5_map {
4860     char *fsys;
4861     char *name;
4862   };
4863   struct stage1_5_map stage1_5_map[] =
4864   {
4865     {"ext2fs",   "/e2fs_stage1_5"},
4866     {"fat",      "/fat_stage1_5"},
4867     {"ufs2",     "/ufs2_stage1_5"},
4868     {"ffs",      "/ffs_stage1_5"},
4869     {"iso9660",  "/iso9660_stage1_5"},
4870     {"jfs",      "/jfs_stage1_5"},
4871     {"minix",    "/minix_stage1_5"},
4872     {"reiserfs", "/reiserfs_stage1_5"},
4873     {"vstafs",   "/vstafs_stage1_5"},
4874     {"xfs",      "/xfs_stage1_5"},
4875     {"ufs",      "/ufs_stage1_5"}
4876   };
4877 
4878   tmp_drive = saved_drive;
4879   tmp_partition = saved_partition;
4880 
4881   /* Check if the user specifies --force-lba.  */
4882   while (1)
4883     {
4884       if (grub_memcmp ("--force-lba", arg, sizeof ("--force-lba") - 1) == 0)
4885 	{
4886 	  is_force_lba = 1;
4887 	  arg = skip_to (0, arg);
4888 	}
4889       else if (grub_memcmp ("--prefix=", arg, sizeof ("--prefix=") - 1) == 0)
4890 	{
4891 	  prefix = arg + sizeof ("--prefix=") - 1;
4892 	  arg = skip_to (0, arg);
4893 	  nul_terminate (prefix);
4894 	}
4895 #ifdef GRUB_UTIL
4896       else if (grub_memcmp ("--stage2=", arg, sizeof ("--stage2=") - 1) == 0)
4897 	{
4898 	  stage2_arg = arg;
4899 	  arg = skip_to (0, arg);
4900 	  nul_terminate (stage2_arg);
4901 	}
4902 #endif /* GRUB_UTIL */
4903       else
4904 	break;
4905     }
4906 
4907   install_ptr = arg;
4908   image_ptr = skip_to (0, install_ptr);
4909 
4910   /* Make sure that INSTALL_PTR is valid.  */
4911   set_device (install_ptr);
4912   if (errnum)
4913     return 1;
4914 
4915   installed_drive = current_drive;
4916   installed_partition = current_partition;
4917 
4918   /* Mount the drive pointed by IMAGE_PTR.  */
4919   if (*image_ptr)
4920     {
4921       /* If the drive/partition where the images reside is specified,
4922 	 get the drive and the partition.  */
4923       set_device (image_ptr);
4924       if (errnum)
4925 	return 1;
4926     }
4927   else
4928     {
4929       /* If omitted, use SAVED_PARTITION and SAVED_DRIVE.  */
4930       current_drive = saved_drive;
4931       current_partition = saved_partition;
4932     }
4933 
4934   image_drive = saved_drive = current_drive;
4935   image_partition = saved_partition = current_partition;
4936 
4937   /* Open it.  */
4938   if (! open_device ())
4939     goto fail;
4940 
4941   /* Check if stage1 exists. If the user doesn't specify the option
4942      `--prefix', attempt /boot/grub and /grub.  */
4943   /* NOTE: It is dangerous to run this command without `--prefix' in the
4944      grub shell, since that affects `--stage2'.  */
4945   if (! prefix)
4946     {
4947       prefix = "/boot/grub";
4948       grub_sprintf (stage1, "%s%s", prefix, "/stage1");
4949       if (! check_file (stage1))
4950 	{
4951 	  errnum = ERR_NONE;
4952 	  prefix = "/grub";
4953 	  grub_sprintf (stage1, "%s%s", prefix, "/stage1");
4954 	  if (! check_file (stage1))
4955 	    goto fail;
4956 	}
4957     }
4958   else
4959     {
4960       grub_sprintf (stage1, "%s%s", prefix, "/stage1");
4961       if (! check_file (stage1))
4962 	goto fail;
4963     }
4964 
4965   /* The prefix was determined.  */
4966   grub_sprintf (stage2, "%s%s", prefix, "/stage2");
4967   grub_sprintf (config_filename, "%s%s", prefix, "/menu.lst");
4968   *real_config_filename = 0;
4969 
4970   /* Check if stage2 exists.  */
4971   if (! check_file (stage2))
4972     goto fail;
4973 
4974   {
4975     char *fsys = fsys_table[fsys_type].name;
4976     int i;
4977     int size = sizeof (stage1_5_map) / sizeof (stage1_5_map[0]);
4978 
4979     /* Iterate finding the same filesystem name as FSYS.  */
4980     for (i = 0; i < size; i++)
4981       if (grub_strcmp (fsys, stage1_5_map[i].fsys) == 0)
4982 	{
4983 	  /* OK, check if the Stage 1.5 exists.  */
4984 	  char stage1_5[64];
4985 
4986 	  grub_sprintf (stage1_5, "%s%s", prefix, stage1_5_map[i].name);
4987 	  if (check_file (stage1_5))
4988 	    {
4989 	      if (embed_stage1_5 (stage1_5,
4990 				    installed_drive, installed_partition)
4991 		  || embed_stage1_5 (stage1_5,
4992 				     image_drive, image_partition))
4993 		{
4994 		  grub_strcpy (real_config_filename, config_filename);
4995 		  sprint_device (image_drive, image_partition);
4996 		  grub_sprintf (config_filename, "%s%s", device, stage2);
4997 		  grub_strcpy (stage2, buffer);
4998 		}
4999 	    }
5000 	  errnum = 0;
5001 	  break;
5002 	}
5003   }
5004 
5005   /* Construct a string that is used by the command "install" as its
5006      arguments.  */
5007   sprint_device (installed_drive, installed_partition);
5008 
5009 #if 1
5010   /* Don't embed a drive number unnecessarily.  */
5011   grub_sprintf (cmd_arg, "%s%s%s%s %s%s %s p %s %s",
5012 		is_force_lba? "--force-lba " : "",
5013 		stage2_arg? stage2_arg : "",
5014 		stage2_arg? " " : "",
5015 		stage1,
5016 		(installed_drive != image_drive) ? "d " : "",
5017 		device,
5018 		stage2,
5019 		config_filename,
5020 		real_config_filename);
5021 #else /* NOT USED */
5022   /* This code was used, because we belived some BIOSes had a problem
5023      that they didn't pass a booting drive correctly. It turned out,
5024      however, stage1 could trash a booting drive when checking LBA support,
5025      because some BIOSes modified the register %dx in INT 13H, AH=48H.
5026      So it becamed unclear whether GRUB should use a pre-defined booting
5027      drive or not. If the problem still exists, it would be necessary to
5028      switch back to this code.  */
5029   grub_sprintf (cmd_arg, "%s%s%s%s d %s %s p %s %s",
5030 		is_force_lba? "--force-lba " : "",
5031 		stage2_arg? stage2_arg : "",
5032 		stage2_arg? " " : "",
5033 		stage1,
5034 		device,
5035 		stage2,
5036 		config_filename,
5037 		real_config_filename);
5038 #endif /* NOT USED */
5039 
5040   /* Notify what will be run.  */
5041   grub_printf (" Running \"install %s\"... ", cmd_arg);
5042 
5043   /* Make sure that SAVED_DRIVE and SAVED_PARTITION are identical
5044      with IMAGE_DRIVE and IMAGE_PARTITION, respectively.  */
5045   saved_drive = image_drive;
5046   saved_partition = image_partition;
5047 
5048   /* Run the command.  */
5049   if (! install_func (cmd_arg, flags))
5050     grub_printf ("succeeded\nDone.\n");
5051   else
5052     grub_printf ("failed\n");
5053 
5054  fail:
5055   saved_drive = tmp_drive;
5056   saved_partition = tmp_partition;
5057   return errnum;
5058 }
5059 
5060 static struct builtin builtin_setup =
5061 {
5062   "setup",
5063   setup_func,
5064   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
5065   "setup [--prefix=DIR] [--stage2=STAGE2_FILE] [--force-lba] INSTALL_DEVICE [IMAGE_DEVICE]",
5066   "Set up the installation of GRUB automatically. This command uses"
5067   " the more flexible command \"install\" in the backend and installs"
5068   " GRUB into the device INSTALL_DEVICE. If IMAGE_DEVICE is specified,"
5069   " then find the GRUB images in the device IMAGE_DEVICE, otherwise"
5070   " use the current \"root device\", which can be set by the command"
5071   " \"root\". If you know that your BIOS should support LBA but GRUB"
5072   " doesn't work in LBA mode, specify the option `--force-lba'."
5073   " If you install GRUB under the grub shell and you cannot unmount the"
5074   " partition where GRUB images reside, specify the option `--stage2'"
5075   " to tell GRUB the file name under your OS."
5076 };
5077 
5078 
5079 #if defined(SUPPORT_SERIAL) || defined(SUPPORT_HERCULES) || defined(SUPPORT_GRAPHICS)
5080 /* terminal */
5081 static int
5082 terminal_func (char *arg, int flags)
5083 {
5084   /* The index of the default terminal in TERM_TABLE.  */
5085   int default_term = -1;
5086   struct term_entry *prev_term = current_term;
5087   int to = -1;
5088   int lines = 0;
5089   int no_message = 0;
5090   unsigned long term_flags = 0;
5091   /* XXX: Assume less than 32 terminals.  */
5092   unsigned long term_bitmap = 0;
5093 
5094   /* Get GNU-style long options.  */
5095   while (1)
5096     {
5097       if (grub_memcmp (arg, "--dumb", sizeof ("--dumb") - 1) == 0)
5098 	term_flags |= TERM_DUMB;
5099       else if (grub_memcmp (arg, "--no-echo", sizeof ("--no-echo") - 1) == 0)
5100 	/* ``--no-echo'' implies ``--no-edit''.  */
5101 	term_flags |= (TERM_NO_ECHO | TERM_NO_EDIT);
5102       else if (grub_memcmp (arg, "--no-edit", sizeof ("--no-edit") - 1) == 0)
5103 	term_flags |= TERM_NO_EDIT;
5104       else if (grub_memcmp (arg, "--timeout=", sizeof ("--timeout=") - 1) == 0)
5105 	{
5106 	  char *val = arg + sizeof ("--timeout=") - 1;
5107 
5108 	  if (! safe_parse_maxint (&val, &to))
5109 	    return 1;
5110 	}
5111       else if (grub_memcmp (arg, "--lines=", sizeof ("--lines=") - 1) == 0)
5112 	{
5113 	  char *val = arg + sizeof ("--lines=") - 1;
5114 
5115 	  if (! safe_parse_maxint (&val, &lines))
5116 	    return 1;
5117 
5118 	  /* Probably less than four is meaningless....  */
5119 	  if (lines < 4)
5120 	    {
5121 	      errnum = ERR_BAD_ARGUMENT;
5122 	      return 1;
5123 	    }
5124 	}
5125       else if (grub_memcmp (arg, "--silent", sizeof ("--silent") - 1) == 0)
5126 	no_message = 1;
5127       else
5128 	break;
5129 
5130       arg = skip_to (0, arg);
5131     }
5132 
5133   /* If no argument is specified, show current setting.  */
5134   if (! *arg)
5135     {
5136       grub_printf ("%s%s%s%s\n",
5137 		   current_term->name,
5138 		   current_term->flags & TERM_DUMB ? " (dumb)" : "",
5139 		   current_term->flags & TERM_NO_EDIT ? " (no edit)" : "",
5140 		   current_term->flags & TERM_NO_ECHO ? " (no echo)" : "");
5141       return 0;
5142     }
5143 
5144   while (*arg)
5145     {
5146       int i;
5147       char *next = skip_to (0, arg);
5148 
5149       nul_terminate (arg);
5150 
5151       for (i = 0; term_table[i].name; i++)
5152 	{
5153 	  if (grub_strcmp (arg, term_table[i].name) == 0)
5154 	    {
5155 	      if (term_table[i].flags & TERM_NEED_INIT)
5156 		{
5157 		  errnum = ERR_DEV_NEED_INIT;
5158 		  return 1;
5159 		}
5160 
5161 	      if (default_term < 0)
5162 		default_term = i;
5163 
5164 	      term_bitmap |= (1 << i);
5165 	      break;
5166 	    }
5167 	}
5168 
5169       if (! term_table[i].name)
5170 	{
5171 	  errnum = ERR_BAD_ARGUMENT;
5172 	  return 1;
5173 	}
5174 
5175       arg = next;
5176     }
5177 
5178   /* If multiple terminals are specified, wait until the user pushes any
5179      key on one of the terminals.  */
5180   if (term_bitmap & ~(1 << default_term))
5181     {
5182       int time1, time2 = -1;
5183 
5184       /* XXX: Disable the pager.  */
5185       count_lines = -1;
5186 
5187       /* Get current time.  */
5188       while ((time1 = getrtsecs ()) == 0xFF)
5189 	;
5190 
5191       /* Wait for a key input.  */
5192       while (to)
5193 	{
5194 	  int i;
5195 
5196 	  for (i = 0; term_table[i].name; i++)
5197 	    {
5198 	      if (term_bitmap & (1 << i))
5199 		{
5200 		  if (term_table[i].checkkey () >= 0)
5201 		    {
5202 		      (void) term_table[i].getkey ();
5203 		      default_term = i;
5204 
5205 		      goto end;
5206 		    }
5207 		}
5208 	    }
5209 
5210 	  /* Prompt the user, once per sec.  */
5211 	  if ((time1 = getrtsecs ()) != time2 && time1 != 0xFF)
5212 	    {
5213 	      if (! no_message)
5214 		{
5215 		  /* Need to set CURRENT_TERM to each of selected
5216 		     terminals.  */
5217 		  for (i = 0; term_table[i].name; i++)
5218 		    if (term_bitmap & (1 << i))
5219 		      {
5220 			current_term = term_table + i;
5221 			grub_printf ("\rPress any key to continue.\n");
5222 		      }
5223 
5224 		  /* Restore CURRENT_TERM.  */
5225 		  current_term = prev_term;
5226 		}
5227 
5228 	      time2 = time1;
5229 	      if (to > 0)
5230 		to--;
5231 	    }
5232 	}
5233     }
5234 
5235  end:
5236   current_term = term_table + default_term;
5237   current_term->flags = term_flags;
5238 
5239   if (lines)
5240     max_lines = lines;
5241   else
5242     max_lines = current_term->max_lines;
5243 
5244   /* If the interface is currently the command-line,
5245      restart it to repaint the screen.  */
5246   if ((current_term != prev_term) && (flags & BUILTIN_CMDLINE)){
5247     if (prev_term->shutdown)
5248       prev_term->shutdown();
5249     if (current_term->startup)
5250       current_term->startup();
5251     grub_longjmp (restart_cmdline_env, 0);
5252   }
5253 
5254   return 0;
5255 }
5256 
5257 static struct builtin builtin_terminal =
5258 {
5259   "terminal",
5260   terminal_func,
5261   BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
5262   "terminal [--dumb] [--no-echo] [--no-edit] [--timeout=SECS] [--lines=LINES] [--silent] [console] [serial] [hercules] [graphics]",
5263   "Select a terminal. When multiple terminals are specified, wait until"
5264   " you push any key to continue. If both console and serial are specified,"
5265   " the terminal to which you input a key first will be selected. If no"
5266   " argument is specified, print current setting. The option --dumb"
5267   " specifies that your terminal is dumb, otherwise, vt100-compatibility"
5268   " is assumed. If you specify --no-echo, input characters won't be echoed."
5269   " If you specify --no-edit, the BASH-like editing feature will be disabled."
5270   " If --timeout is present, this command will wait at most for SECS"
5271   " seconds. The option --lines specifies the maximum number of lines."
5272   " The option --silent is used to suppress messages."
5273 };
5274 #endif /* SUPPORT_SERIAL || SUPPORT_HERCULES || SUPPORT_GRAPHICS */
5275 
5276 
5277 #ifdef SUPPORT_SERIAL
5278 static int
5279 terminfo_func (char *arg, int flags)
5280 {
5281   struct terminfo term;
5282 
5283   if (*arg)
5284     {
5285       struct
5286       {
5287 	const char *name;
5288 	char *var;
5289       }
5290       options[] =
5291 	{
5292 	  {"--name=", term.name},
5293 	  {"--cursor-address=", term.cursor_address},
5294 	  {"--clear-screen=", term.clear_screen},
5295 	  {"--enter-standout-mode=", term.enter_standout_mode},
5296 	  {"--exit-standout-mode=", term.exit_standout_mode}
5297 	};
5298 
5299       grub_memset (&term, 0, sizeof (term));
5300 
5301       while (*arg)
5302 	{
5303 	  int i;
5304 	  char *next = skip_to (0, arg);
5305 
5306 	  nul_terminate (arg);
5307 
5308 	  for (i = 0; i < sizeof (options) / sizeof (options[0]); i++)
5309 	    {
5310 	      const char *name = options[i].name;
5311 	      int len = grub_strlen (name);
5312 
5313 	      if (! grub_memcmp (arg, name, len))
5314 		{
5315 		  grub_strcpy (options[i].var, ti_unescape_string (arg + len));
5316 		  break;
5317 		}
5318 	    }
5319 
5320 	  if (i == sizeof (options) / sizeof (options[0]))
5321 	    {
5322 	      errnum = ERR_BAD_ARGUMENT;
5323 	      return errnum;
5324 	    }
5325 
5326 	  arg = next;
5327 	}
5328 
5329       if (term.name[0] == 0 || term.cursor_address[0] == 0)
5330 	{
5331 	  errnum = ERR_BAD_ARGUMENT;
5332 	  return errnum;
5333 	}
5334 
5335       ti_set_term (&term);
5336     }
5337   else
5338     {
5339       /* No option specifies printing out current settings.  */
5340       ti_get_term (&term);
5341 
5342       grub_printf ("name=%s\n",
5343 		   ti_escape_string (term.name));
5344       grub_printf ("cursor_address=%s\n",
5345 		   ti_escape_string (term.cursor_address));
5346       grub_printf ("clear_screen=%s\n",
5347 		   ti_escape_string (term.clear_screen));
5348       grub_printf ("enter_standout_mode=%s\n",
5349 		   ti_escape_string (term.enter_standout_mode));
5350       grub_printf ("exit_standout_mode=%s\n",
5351 		   ti_escape_string (term.exit_standout_mode));
5352     }
5353 
5354   return 0;
5355 }
5356 
5357 static struct builtin builtin_terminfo =
5358 {
5359   "terminfo",
5360   terminfo_func,
5361   BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
5362   "terminfo [--name=NAME --cursor-address=SEQ [--clear-screen=SEQ]"
5363   " [--enter-standout-mode=SEQ] [--exit-standout-mode=SEQ]]",
5364 
5365   "Define the capabilities of your terminal. Use this command to"
5366   " define escape sequences, if it is not vt100-compatible."
5367   " You may use \\e for ESC and ^X for a control character."
5368   " If no option is specified, the current settings are printed."
5369 };
5370 #endif /* SUPPORT_SERIAL */
5371 
5372 
5373 /* testload */
5374 static int
5375 testload_func (char *arg, int flags)
5376 {
5377   int i;
5378 
5379   kernel_type = KERNEL_TYPE_NONE;
5380 
5381   if (! grub_open (arg))
5382     return 1;
5383 
5384   disk_read_hook = disk_read_print_func;
5385 
5386   /* Perform filesystem test on the specified file.  */
5387   /* Read whole file first. */
5388   grub_printf ("Whole file: ");
5389 
5390   grub_read ((char *) RAW_ADDR (0x100000), -1);
5391 
5392   /* Now compare two sections of the file read differently.  */
5393 
5394   for (i = 0; i < 0x10ac0; i++)
5395     {
5396       *((unsigned char *) RAW_ADDR (0x200000 + i)) = 0;
5397       *((unsigned char *) RAW_ADDR (0x300000 + i)) = 1;
5398     }
5399 
5400   /* First partial read.  */
5401   grub_printf ("\nPartial read 1: ");
5402 
5403   grub_seek (0);
5404   grub_read ((char *) RAW_ADDR (0x200000), 0x7);
5405   grub_read ((char *) RAW_ADDR (0x200007), 0x100);
5406   grub_read ((char *) RAW_ADDR (0x200107), 0x10);
5407   grub_read ((char *) RAW_ADDR (0x200117), 0x999);
5408   grub_read ((char *) RAW_ADDR (0x200ab0), 0x10);
5409   grub_read ((char *) RAW_ADDR (0x200ac0), 0x10000);
5410 
5411   /* Second partial read.  */
5412   grub_printf ("\nPartial read 2: ");
5413 
5414   grub_seek (0);
5415   grub_read ((char *) RAW_ADDR (0x300000), 0x10000);
5416   grub_read ((char *) RAW_ADDR (0x310000), 0x10);
5417   grub_read ((char *) RAW_ADDR (0x310010), 0x7);
5418   grub_read ((char *) RAW_ADDR (0x310017), 0x10);
5419   grub_read ((char *) RAW_ADDR (0x310027), 0x999);
5420   grub_read ((char *) RAW_ADDR (0x3109c0), 0x100);
5421 
5422   grub_printf ("\nHeader1 = 0x%x, next = 0x%x, next = 0x%x, next = 0x%x\n",
5423 	       *((int *) RAW_ADDR (0x200000)),
5424 	       *((int *) RAW_ADDR (0x200004)),
5425 	       *((int *) RAW_ADDR (0x200008)),
5426 	       *((int *) RAW_ADDR (0x20000c)));
5427 
5428   grub_printf ("Header2 = 0x%x, next = 0x%x, next = 0x%x, next = 0x%x\n",
5429 	       *((int *) RAW_ADDR (0x300000)),
5430 	       *((int *) RAW_ADDR (0x300004)),
5431 	       *((int *) RAW_ADDR (0x300008)),
5432 	       *((int *) RAW_ADDR (0x30000c)));
5433 
5434   for (i = 0; i < 0x10ac0; i++)
5435     if (*((unsigned char *) RAW_ADDR (0x200000 + i))
5436 	!= *((unsigned char *) RAW_ADDR (0x300000 + i)))
5437       break;
5438 
5439   grub_printf ("Max is 0x10ac0: i=0x%x, filepos=0x%x\n", i, filepos);
5440   disk_read_hook = 0;
5441   grub_close ();
5442   return 0;
5443 }
5444 
5445 static struct builtin builtin_testload =
5446 {
5447   "testload",
5448   testload_func,
5449   BUILTIN_CMDLINE,
5450   "testload FILE",
5451   "Read the entire contents of FILE in several different ways and"
5452   " compares them, to test the filesystem code. The output is somewhat"
5453   " cryptic, but if no errors are reported and the final `i=X,"
5454   " filepos=Y' reading has X and Y equal, then it is definitely"
5455   " consistent, and very likely works correctly subject to a"
5456   " consistent offset error. If this test succeeds, then a good next"
5457   " step is to try loading a kernel."
5458 };
5459 
5460 
5461 /* testvbe MODE */
5462 static int
5463 testvbe_func (char *arg, int flags)
5464 {
5465   int mode_number;
5466   struct vbe_controller controller;
5467   struct vbe_mode mode;
5468 
5469   if (! *arg)
5470     {
5471       errnum = ERR_BAD_ARGUMENT;
5472       return 1;
5473     }
5474 
5475   if (! safe_parse_maxint (&arg, &mode_number))
5476     return 1;
5477 
5478   /* Preset `VBE2'.  */
5479   grub_memmove (controller.signature, "VBE2", 4);
5480 
5481   /* Detect VBE BIOS.  */
5482   if (get_vbe_controller_info (&controller) != 0x004F)
5483     {
5484       grub_printf (" VBE BIOS is not present.\n");
5485       return 0;
5486     }
5487 
5488   if (controller.version < 0x0200)
5489     {
5490       grub_printf (" VBE version %d.%d is not supported.\n",
5491 		   (int) (controller.version >> 8),
5492 		   (int) (controller.version & 0xFF));
5493       return 0;
5494     }
5495 
5496   if (get_vbe_mode_info (mode_number, &mode) != 0x004F
5497       || (mode.mode_attributes & 0x0091) != 0x0091)
5498     {
5499       grub_printf (" Mode 0x%x is not supported.\n", mode_number);
5500       return 0;
5501     }
5502 
5503   /* Now trip to the graphics mode.  */
5504   if (set_vbe_mode (mode_number | (1 << 14)) != 0x004F)
5505     {
5506       grub_printf (" Switching to Mode 0x%x failed.\n", mode_number);
5507       return 0;
5508     }
5509 
5510   /* Draw something on the screen...  */
5511   {
5512     unsigned char *base_buf = (unsigned char *) mode.phys_base;
5513     int scanline = controller.version >= 0x0300
5514       ? mode.linear_bytes_per_scanline : mode.bytes_per_scanline;
5515     /* FIXME: this assumes that any depth is a modulo of 8.  */
5516     int bpp = mode.bits_per_pixel / 8;
5517     int width = mode.x_resolution;
5518     int height = mode.y_resolution;
5519     int x, y;
5520     unsigned color = 0;
5521 
5522     /* Iterate drawing on the screen, until the user hits any key.  */
5523     while (checkkey () == -1)
5524       {
5525 	for (y = 0; y < height; y++)
5526 	  {
5527 	    unsigned char *line_buf = base_buf + scanline * y;
5528 
5529 	    for (x = 0; x < width; x++)
5530 	      {
5531 		unsigned char *buf = line_buf + bpp * x;
5532 		int i;
5533 
5534 		for (i = 0; i < bpp; i++, buf++)
5535 		  *buf = (color >> (i * 8)) & 0xff;
5536 	      }
5537 
5538 	    color++;
5539 	  }
5540       }
5541 
5542     /* Discard the input.  */
5543     getkey ();
5544   }
5545 
5546   /* Back to the default text mode.  */
5547   if (set_vbe_mode (0x03) != 0x004F)
5548     {
5549       /* Why?!  */
5550       grub_reboot ();
5551     }
5552 
5553   return 0;
5554 }
5555 
5556 static struct builtin builtin_testvbe =
5557 {
5558   "testvbe",
5559   testvbe_func,
5560   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
5561   "testvbe MODE",
5562   "Test the VBE mode MODE. Hit any key to return."
5563 };
5564 
5565 
5566 #ifdef SUPPORT_NETBOOT
5567 /* tftpserver */
5568 static int
5569 tftpserver_func (char *arg, int flags)
5570 {
5571   if (! *arg || ! ifconfig (0, 0, 0, arg))
5572     {
5573       errnum = ERR_BAD_ARGUMENT;
5574       return 1;
5575     }
5576 
5577   print_network_configuration ();
5578   return 0;
5579 }
5580 
5581 static struct builtin builtin_tftpserver =
5582 {
5583   "tftpserver",
5584   tftpserver_func,
5585   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
5586   "tftpserver IPADDR",
5587   "Override the TFTP server address."
5588 };
5589 #endif /* SUPPORT_NETBOOT */
5590 
5591 
5592 /* timeout */
5593 static int
5594 timeout_func (char *arg, int flags)
5595 {
5596   if (! safe_parse_maxint (&arg, &grub_timeout))
5597     return 1;
5598 
5599   return 0;
5600 }
5601 
5602 static struct builtin builtin_timeout =
5603 {
5604   "timeout",
5605   timeout_func,
5606   BUILTIN_MENU,
5607 #if 0
5608   "timeout SEC",
5609   "Set a timeout, in SEC seconds, before automatically booting the"
5610   " default entry (normally the first entry defined)."
5611 #endif
5612 };
5613 
5614 
5615 /* title */
5616 static int
5617 title_func (char *arg, int flags)
5618 {
5619   /* This function is not actually used at least currently.  */
5620   return 0;
5621 }
5622 
5623 static struct builtin builtin_title =
5624 {
5625   "title",
5626   title_func,
5627   BUILTIN_TITLE,
5628 #if 0
5629   "title [NAME ...]",
5630   "Start a new boot entry, and set its name to the contents of the"
5631   " rest of the line, starting with the first non-space character."
5632 #endif
5633 };
5634 
5635 
5636 /* unhide */
5637 static int
5638 unhide_func (char *arg, int flags)
5639 {
5640   if (! set_device (arg))
5641     return 1;
5642 
5643   if (! set_partition_hidden_flag (0))
5644     return 1;
5645 
5646   return 0;
5647 }
5648 
5649 static struct builtin builtin_unhide =
5650 {
5651   "unhide",
5652   unhide_func,
5653   BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
5654   "unhide PARTITION",
5655   "Unhide PARTITION by clearing the \"hidden\" bit in its"
5656   " partition type code."
5657 };
5658 
5659 
5660 /* uppermem */
5661 static int
5662 uppermem_func (char *arg, int flags)
5663 {
5664   if (! safe_parse_maxint (&arg, (int *) &mbi.mem_upper))
5665     return 1;
5666 
5667   mbi.flags &= ~MB_INFO_MEM_MAP;
5668   return 0;
5669 }
5670 
5671 static struct builtin builtin_uppermem =
5672 {
5673   "uppermem",
5674   uppermem_func,
5675   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
5676   "uppermem KBYTES",
5677   "Force GRUB to assume that only KBYTES kilobytes of upper memory are"
5678   " installed.  Any system address range maps are discarded."
5679 };
5680 
5681 
5682 /* vbeprobe */
5683 static int
5684 vbeprobe_func (char *arg, int flags)
5685 {
5686   struct vbe_controller controller;
5687   unsigned short *mode_list;
5688   int mode_number = -1;
5689 
5690   auto unsigned long vbe_far_ptr_to_linear (unsigned long);
5691 
5692   unsigned long vbe_far_ptr_to_linear (unsigned long ptr)
5693     {
5694       unsigned short seg = (ptr >> 16);
5695       unsigned short off = (ptr & 0xFFFF);
5696 
5697       return (seg << 4) + off;
5698     }
5699 
5700   if (*arg)
5701     {
5702       if (! safe_parse_maxint (&arg, &mode_number))
5703 	return 1;
5704     }
5705 
5706   /* Set the signature to `VBE2', to obtain VBE 3.0 information.  */
5707   grub_memmove (controller.signature, "VBE2", 4);
5708 
5709   if (get_vbe_controller_info (&controller) != 0x004F)
5710     {
5711       grub_printf (" VBE BIOS is not present.\n");
5712       return 0;
5713     }
5714 
5715   /* Check the version.  */
5716   if (controller.version < 0x0200)
5717     {
5718       grub_printf (" VBE version %d.%d is not supported.\n",
5719 		   (int) (controller.version >> 8),
5720 		   (int) (controller.version & 0xFF));
5721       return 0;
5722     }
5723 
5724   /* Print some information.  */
5725   grub_printf (" VBE version %d.%d\n",
5726 	       (int) (controller.version >> 8),
5727 	       (int) (controller.version & 0xFF));
5728 
5729   /* Iterate probing modes.  */
5730   for (mode_list
5731 	 = (unsigned short *) vbe_far_ptr_to_linear (controller.video_mode);
5732        *mode_list != 0xFFFF;
5733        mode_list++)
5734     {
5735       struct vbe_mode mode;
5736 
5737       if (get_vbe_mode_info (*mode_list, &mode) != 0x004F)
5738 	continue;
5739 
5740       /* Skip this, if this is not supported or linear frame buffer
5741 	 mode is not support.  */
5742       if ((mode.mode_attributes & 0x0081) != 0x0081)
5743 	continue;
5744 
5745       if (mode_number == -1 || mode_number == *mode_list)
5746 	{
5747 	  char *model;
5748 	  switch (mode.memory_model)
5749 	    {
5750 	    case 0x00: model = "Text"; break;
5751 	    case 0x01: model = "CGA graphics"; break;
5752 	    case 0x02: model = "Hercules graphics"; break;
5753 	    case 0x03: model = "Planar"; break;
5754 	    case 0x04: model = "Packed pixel"; break;
5755 	    case 0x05: model = "Non-chain 4, 256 color"; break;
5756 	    case 0x06: model = "Direct Color"; break;
5757 	    case 0x07: model = "YUV"; break;
5758 	    default: model = "Unknown"; break;
5759 	    }
5760 
5761 	  grub_printf ("  0x%x: %s, %ux%ux%u\n",
5762 		       (unsigned) *mode_list,
5763 		       model,
5764 		       (unsigned) mode.x_resolution,
5765 		       (unsigned) mode.y_resolution,
5766 		       (unsigned) mode.bits_per_pixel);
5767 
5768 	  if (mode_number != -1)
5769 	    break;
5770 	}
5771     }
5772 
5773   if (mode_number != -1 && mode_number != *mode_list)
5774     grub_printf ("  Mode 0x%x is not found or supported.\n", mode_number);
5775 
5776   return 0;
5777 }
5778 
5779 static struct builtin builtin_vbeprobe =
5780 {
5781   "vbeprobe",
5782   vbeprobe_func,
5783   BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
5784   "vbeprobe [MODE]",
5785   "Probe VBE information. If the mode number MODE is specified, show only"
5786   " the information about only the mode."
5787 };
5788 
5789 
5790 /* The table of builtin commands. Sorted in dictionary order.  */
5791 struct builtin *builtin_table[] =
5792 {
5793 #ifdef SUPPORT_GRAPHICS
5794   &builtin_background,
5795 #endif
5796   &builtin_blocklist,
5797   &builtin_boot,
5798   &builtin_bootfs,
5799 #ifdef SUPPORT_NETBOOT
5800   &builtin_bootp,
5801 #endif /* SUPPORT_NETBOOT */
5802   &builtin_cat,
5803   &builtin_chainloader,
5804   &builtin_clear,
5805   &builtin_cmp,
5806   &builtin_color,
5807   &builtin_configfile,
5808   &builtin_debug,
5809   &builtin_default,
5810 #ifdef GRUB_UTIL
5811   &builtin_device,
5812 #endif /* GRUB_UTIL */
5813 #ifdef SUPPORT_NETBOOT
5814   &builtin_dhcp,
5815 #endif /* SUPPORT_NETBOOT */
5816   &builtin_displayapm,
5817   &builtin_displaymem,
5818 #ifdef GRUB_UTIL
5819   &builtin_dump,
5820 #endif /* GRUB_UTIL */
5821   &builtin_embed,
5822   &builtin_fallback,
5823   &builtin_find,
5824   &builtin_findroot,
5825 #ifdef SUPPORT_GRAPHICS
5826   &builtin_foreground,
5827 #endif
5828   &builtin_fstest,
5829   &builtin_geometry,
5830   &builtin_halt,
5831   &builtin_help,
5832   &builtin_hiddenmenu,
5833   &builtin_hide,
5834 #ifdef SUPPORT_NETBOOT
5835   &builtin_ifconfig,
5836 #endif /* SUPPORT_NETBOOT */
5837   &builtin_impsprobe,
5838   &builtin_initrd,
5839   &builtin_install,
5840   &builtin_ioprobe,
5841   &builtin_kernel,
5842   &builtin_kernel_dollar,
5843   &builtin_lock,
5844   &builtin_makeactive,
5845   &builtin_map,
5846 #ifdef USE_MD5_PASSWORDS
5847   &builtin_md5crypt,
5848 #endif /* USE_MD5_PASSWORDS */
5849   &builtin_min_mem64,
5850   &builtin_module,
5851   &builtin_module_dollar,
5852   &builtin_modulenounzip,
5853   &builtin_pager,
5854   &builtin_partnew,
5855   &builtin_parttype,
5856   &builtin_password,
5857   &builtin_pause,
5858 #if defined(RPC_DEBUG) && defined(SUPPORT_NETBOOT)
5859   &builtin_portmap,
5860 #endif /* RPC_DEBUG && SUPPORT_NETBOOT */
5861 #ifdef GRUB_UTIL
5862   &builtin_quit,
5863 #endif /* GRUB_UTIL */
5864 #ifdef SUPPORT_NETBOOT
5865   &builtin_rarp,
5866 #endif /* SUPPORT_NETBOOT */
5867   &builtin_read,
5868   &builtin_reboot,
5869   &builtin_root,
5870   &builtin_rootnoverify,
5871   &builtin_savedefault,
5872 #ifdef SUPPORT_SERIAL
5873   &builtin_serial,
5874 #endif /* SUPPORT_SERIAL */
5875   &builtin_setkey,
5876   &builtin_setup,
5877 #ifdef SUPPORT_GRAPHICS
5878   &builtin_splashimage,
5879 #endif /* SUPPORT_GRAPHICS */
5880 #if defined(SUPPORT_SERIAL) || defined(SUPPORT_HERCULES) || defined(SUPPORT_GRAPHICS)
5881   &builtin_terminal,
5882 #endif /* SUPPORT_SERIAL || SUPPORT_HERCULES || SUPPORT_GRAPHICS */
5883 #ifdef SUPPORT_SERIAL
5884   &builtin_terminfo,
5885 #endif /* SUPPORT_SERIAL */
5886   &builtin_testload,
5887   &builtin_testvbe,
5888 #ifdef SUPPORT_NETBOOT
5889   &builtin_tftpserver,
5890 #endif /* SUPPORT_NETBOOT */
5891   &builtin_timeout,
5892   &builtin_title,
5893   &builtin_unhide,
5894   &builtin_uppermem,
5895   &builtin_vbeprobe,
5896   &builtin_verbose,
5897   0
5898 };
5899