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