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