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