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