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