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