builtins.c revision 266095ca3ea7169c311dab7af409e34de271db16
/* builtins.c - the GRUB builtin commands */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc.
*
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
WITHOUT_LIBC_STUBS here. */
#ifdef GRUB_UTIL
# include <stdio.h>
#endif
#include <shared.h>
#include <filesys.h>
#include <term.h>
#ifdef SUPPORT_NETBOOT
# include <grub.h>
#endif
#ifdef SUPPORT_SERIAL
# include <serial.h>
# include <terminfo.h>
#endif
#ifdef GRUB_UTIL
# include <device.h>
#else /* ! GRUB_UTIL */
# include <apic.h>
# include <smp-imps.h>
#endif /* ! GRUB_UTIL */
#ifdef USE_MD5_PASSWORDS
# include <md5.h>
#endif
/* The type of kernel loaded. */
/* The boot device. */
static int bootdev;
/* True when the debug mode is turned on, and false
when it is turned off. */
int debug = 0;
/* The default entry. */
int default_entry = 0;
/* The fallback entry. */
int fallback_entryno;
/* The number of current entry. */
int current_entryno;
/* The address for Multiboot command-line buffer. */
static char *mb_cmdline;
/* The password. */
char *password;
/* The password type. */
/* The flag for indicating that the user is authoritative. */
int auth = 0;
/* The timeout. */
int grub_timeout = -1;
/* Whether to show the menu or not. */
int show_menu = 1;
/* The BIOS drive map. */
/* Prototypes for allowing straightfoward calling of builtins functions
inside other functions. */
#ifdef SUPPORT_NETBOOT
static void solaris_config_file (void);
#endif
extern void __enable_execute_stack (void *);
void
__enable_execute_stack (void *addr)
{
}
#endif /* __sun && !GRUB_UTIL */
/* Initialize the data for builtins. */
void
init_builtins (void)
{
/* BSD and chainloading evil hacks! */
bootdev = set_bootdev (0);
mb_cmdline = (char *) MB_CMDLINE_BUF;
}
/* Initialize the data for the configuration file. */
void
init_config (void)
{
default_entry = 0;
password = 0;
fallback_entryno = -1;
fallback_entries[0] = -1;
grub_timeout = -1;
}
/* Check a password for correctness. Returns 0 if password was
correct, and a value != 0 for error, similarly to strcmp. */
int
{
switch (type)
{
case PASSWORD_PLAIN:
#ifdef USE_MD5_PASSWORDS
case PASSWORD_MD5:
#endif
default:
/* unsupported password type: be secure */
return 1;
}
}
/* Print which sector is read when loading a file. */
static void
{
}
/* blocklist */
static int
{
int start_sector;
int num_sectors = 0;
int num_entries = 0;
int last_length = 0;
/* Collect contiguous blocks into one entry as many as possible,
and print the blocklist notation on the screen. */
{
if (num_sectors > 0)
{
{
num_sectors++;
return;
}
else
{
if (last_length == SECTOR_SIZE)
else if (num_sectors > 1)
else
num_entries++;
num_sectors = 0;
}
}
if (offset > 0)
{
num_entries++;
}
else
{
num_sectors = 1;
}
}
/* Open the file. */
return 1;
/* Print the device name. */
grub_printf ("(%cd%d",
current_drive & ~0x80);
grub_printf (")");
/* Read in the whole file to DUMMY. */
goto fail;
/* The last entry may not be printed yet. Don't check if it is a
* full sector, since it doesn't matter if we read too much. */
if (num_sectors > 0)
grub_printf ("\n");
fail:
disk_read_hook = 0;
grub_close ();
return errnum;
}
static struct builtin builtin_blocklist =
{
"blocklist",
"blocklist FILE",
"Print the blocklist notation of the file FILE."
};
/* boot */
static int
{
/* Clear the int15 handler if we can boot the kernel successfully.
This assumes that the boot code never fails only if KERNEL_TYPE is
not KERNEL_TYPE_NONE. Is this assumption is bad? */
if (kernel_type != KERNEL_TYPE_NONE)
#ifdef SUPPORT_NETBOOT
/* Shut down the networking. */
cleanup_net ();
#endif
switch (kernel_type)
{
case KERNEL_TYPE_FREEBSD:
case KERNEL_TYPE_NETBSD:
/* *BSD */
break;
case KERNEL_TYPE_LINUX:
/* Linux */
linux_boot ();
break;
case KERNEL_TYPE_BIG_LINUX:
/* Big Linux */
big_linux_boot ();
break;
case KERNEL_TYPE_CHAINLOADER:
/* Chainloader */
/* Check if we should set the int13 handler. */
if (bios_drive_map[0] != 0)
{
int i;
/* Search for SAVED_DRIVE. */
for (i = 0; i < DRIVE_MAP_SIZE; i++)
{
if (! bios_drive_map[i])
break;
{
/* Exchage SAVED_DRIVE with the mapped drive. */
break;
}
}
/* Set the handler. This is somewhat dangerous. */
}
gateA20 (0);
break;
case KERNEL_TYPE_MULTIBOOT:
/* Multiboot */
#ifdef SUPPORT_NETBOOT
#ifdef SOLARIS_NETBOOT
if (current_drive == NETWORK_DRIVE) {
/*
* XXX Solaris hack: use drive_info to pass network information
* Turn off the flag bit to the loader is technically
* multiboot compliant.
*/
}
#endif /* SOLARIS_NETBOOT */
#endif
break;
default:
return 1;
}
return 0;
}
static struct builtin builtin_boot =
{
"boot",
"boot",
"Boot the OS/chain-loader which has been loaded."
};
#ifdef SUPPORT_NETBOOT
/* bootp */
static int
{
int with_configfile = 0;
== 0)
{
with_configfile = 1;
}
if (! bootp ())
{
return 1;
}
/* Notify the configuration. */
/* XXX: this can cause an endless loop, but there is no easy way to
detect such a loop unfortunately. */
if (with_configfile)
return 0;
}
static struct builtin builtin_bootp =
{
"bootp",
"bootp [--with-configfile]",
"Initialize a network device via BOOTP. If the option `--with-configfile'"
" is given, try to load a configuration file specified by the 150 vendor"
" tag."
};
#endif /* SUPPORT_NETBOOT */
/* cat */
static int
{
char c;
return 1;
while (grub_read (&c, 1))
{
/* Because running "cat" with a binary file can confuse the terminal,
print only some characters as they are. */
grub_putchar (c);
else
grub_putchar ('?');
}
grub_close ();
return 0;
}
static struct builtin builtin_cat =
{
"cat",
"cat FILE",
"Print the contents of the file FILE."
};
/* chainloader */
static int
{
int force = 0;
/* If the option `--force' is specified? */
{
force = 1;
}
/* Open the file. */
{
return 1;
}
/* Read the first block. */
{
grub_close ();
/* This below happens, if a file whose size is less than 512 bytes
is loaded. */
return 1;
}
/* If not loading it forcibly, check for the signature. */
if (! force
&& (*((unsigned short *) (BOOTSEC_LOCATION + BOOTSEC_SIG_OFFSET))
!= BOOTSEC_SIGNATURE))
{
grub_close ();
return 1;
}
grub_close ();
/* XXX: Windows evil hack. For now, only the first five letters are
checked. */
"MSWIN", 5))
*((unsigned long *) (BOOTSEC_LOCATION + BOOTSEC_BPB_HIDDEN_SECTORS))
= part_start;
return 0;
}
static struct builtin builtin_chainloader =
{
"chainloader",
"chainloader [--force] FILE",
"Load the chain-loader FILE. If --force is specified, then load it"
" forcibly, whether the boot loader signature is present or not."
};
/* This function could be used to debug new filesystem code. Put a file
in the new filesystem and the same file in a well-tested filesystem.
Then, run "cmp" with the files. If no output is obtained, probably
the code is good, otherwise investigate what's wrong... */
/* cmp FILE1 FILE2 */
static int
{
/* The filenames. */
/* The addresses. */
int i;
/* The size of the file. */
int size;
/* Get the filenames from ARG. */
{
return 1;
}
/* Terminate the filenames for convenience. */
/* Read the whole data from FILE1. */
return 1;
/* Get the size. */
{
grub_close ();
return 1;
}
grub_close ();
/* Read the whole data from FILE2. */
return 1;
/* Check if the size of FILE2 is equal to the one of FILE2. */
{
grub_printf ("Differ in size: 0x%x [%s], 0x%x [%s]\n",
grub_close ();
return 0;
}
{
grub_close ();
return 1;
}
grub_close ();
/* Now compare ADDR1 with ADDR2. */
for (i = 0; i < size; i++)
{
grub_printf ("Differ at the offset %d: 0x%x [%s], 0x%x [%s]\n",
}
return 0;
}
static struct builtin builtin_cmp =
{
"cmp",
"cmp FILE1 FILE2",
"Compare the file FILE1 with the FILE2 and inform the different values"
" if any."
};
/* color */
/* Set new colors used for the menu interface. Support two methods to
specify a color name: a direct integer representation and a symbolic
color name. An example of the latter is "blink-light-gray/blue". */
static int
{
char *normal;
char *highlight;
int new_normal_color;
int new_highlight_color;
static char *color_list[16] =
{
"black",
"blue",
"green",
"cyan",
"red",
"magenta",
"brown",
"light-gray",
"dark-gray",
"light-blue",
"light-green",
"light-cyan",
"light-red",
"light-magenta",
"yellow",
"white"
};
/* Convert the color name STR into the magical number. */
static int color_number (char *str)
{
char *ptr;
int i;
int color = 0;
/* Find the separator. */
;
/* If not found, return -1. */
if (! *ptr)
return -1;
/* Terminate the string STR. */
*ptr++ = 0;
/* If STR contains the prefix "blink-", then set the `blink' bit
in COLOR. */
{
color = 0x80;
str += 6;
}
/* Search for the color name. */
for (i = 0; i < 16; i++)
{
color |= i;
break;
}
if (i == 16)
return -1;
nul_terminate (str);
/* Search for the color name. */
for (i = 0; i < 8; i++)
{
color |= i << 4;
break;
}
if (i == 8)
return -1;
return color;
}
return 1;
/* The second argument is optional, so set highlight_color
to inverted NORMAL_COLOR. */
if (! *highlight)
else
{
if (new_highlight_color < 0
return 1;
}
if (current_term->setcolor)
return 0;
}
static struct builtin builtin_color =
{
"color",
"color NORMAL [HIGHLIGHT]",
"Change the menu colors. The color NORMAL is used for most"
" lines in the menu, and the color HIGHLIGHT is used to highlight the"
" line where the cursor points. If you omit HIGHLIGHT, then the"
" inverted color of NORMAL is used for the highlighted line."
" A symbolic color name must be one of these: black, blue, green,"
" cyan, red, magenta, brown, light-gray, dark-gray, light-blue,"
" light-green, light-cyan, light-red, light-magenta, yellow and white."
" But only the first eight names can be used for BG. You can prefix"
" \"blink-\" to FG if you want a blinking foreground color."
};
/* configfile */
static int
{
char *new_config = config_file;
/* Check if the file ARG is present. */
return 1;
grub_close ();
/* Copy ARG to CONFIG_FILE. */
while ((*new_config++ = *arg++) != 0)
;
#ifdef GRUB_UTIL
/* Force to load the configuration file. */
use_config_file = 1;
#endif
/* Make sure that the user will not be authoritative. */
auth = 0;
/* Restart cmain. */
grub_longjmp (restart_env, 0);
/* Never reach here. */
return 0;
}
static struct builtin builtin_configfile =
{
"configfile",
"configfile FILE",
"Load FILE as the configuration file."
};
/* debug */
static int
{
if (debug)
{
debug = 0;
grub_printf (" Debug mode is turned off\n");
}
else
{
debug = 1;
grub_printf (" Debug mode is turned on\n");
}
return 0;
}
static struct builtin builtin_debug =
{
"debug",
"debug",
};
/* default */
static int
{
#ifndef SUPPORT_DISKLESS
{
return 0;
}
#endif /* SUPPORT_DISKLESS */
return 1;
return 0;
}
static struct builtin builtin_default =
{
"default",
#if 0
"default [NUM | `saved']",
"Set the default entry to entry number NUM (if not specified, it is"
" 0, the first entry) or the entry number saved by savedefault."
#endif
};
#ifdef GRUB_UTIL
/* device */
static int
{
char *device;
/* Get the drive number from DRIVE. */
if (! set_device (drive))
return 1;
/* Get the device argument. */
/* Terminate DEVICE. */
{
return 1;
}
return 0;
}
static struct builtin builtin_device =
{
"device",
"device DRIVE DEVICE",
"Specify DEVICE as the actual drive for a BIOS drive DRIVE. This command"
" can be used only in the grub shell."
};
#endif /* GRUB_UTIL */
#ifdef SUPPORT_NETBOOT
/* Debug Function for RPC */
#ifdef RPC_DEBUG
/* portmap */
static int
{
if (! grub_eth_probe ()){
grub_printf ("No ethernet card found.\n");
return 1;
}
grub_printf("Error prog number\n");
return 1;
}
grub_printf("Error ver number\n");
return 1;
}
return 0;
}
static struct builtin builtin_portmap =
{
"portmap",
"portmap prog_number vers_number",
"Do portmap with the prog_number and vers_number"
};
#endif /* RPC_DEBUG */
/* dhcp */
static int
{
int with_configfile = 0;
== 0)
{
with_configfile = 1;
}
if (! dhcp ())
{
return 1;
}
/* Notify the configuration. */
/* XXX: this can cause an endless loop, but there is no easy way to
detect such a loop unfortunately. */
if (with_configfile)
else
return 0;
}
static void solaris_config_file (void)
{
static char menufile[64];
static char hexdigit[] = "0123456789ABCDEF";
char *c = menufile;
int i;
/* if config_file is from DHCP option 150, keep the setting */
return;
/* default solaris configfile name menu.lst.01<ether_addr> */
grub_strcpy(c, "menu.lst.01");
c += grub_strlen(c);
for (i = 0; i < ETH_ALEN; i++) {
*c++ = hexdigit[b >> 4];
*c++ = hexdigit[b & 0xf];
}
*c = 0;
/* If the file exists, make it the default. Else, fallback
* to what it was.
*/
grub_close();
} else {
char *cp = config_file;
/* skip leading slashes for tftp */
while (*cp == '/')
++cp;
}
}
static struct builtin builtin_dhcp =
{
"dhcp",
"dhcp",
"Initialize a network device via DHCP."
};
#endif /* SUPPORT_NETBOOT */
#ifdef SUPPORT_GRAPHICS
char splashimage[64];
int i;
/* filename can only be 64 characters due to our buffer size */
return 1;
if (flags == BUILTIN_CMDLINE) {
return 1;
grub_close();
}
/* get rid of TERM_NEED_INIT from the graphics terminal. */
for (i = 0; term_table[i].name; i++) {
break;
}
}
graphics_end();
graphics_cls();
}
/* FIXME: should we be explicitly switching the terminal as a
* side effect here? */
return 0;
}
static struct builtin builtin_splashimage =
{
"splashimage",
"splashimage FILE",
"Load FILE as the background image when in graphics mode."
};
/* foreground */
static int
{
if (graphics_inited)
graphics_set_palette(15, r, g, b);
return (0);
}
return (1);
}
static struct builtin builtin_foreground =
{
"foreground",
"foreground RRGGBB",
"Sets the foreground color when in graphics mode."
"RR is red, GG is green, and BB blue. Numbers must be in hexadecimal."
};
/* background */
static int
{
if (graphics_inited)
graphics_set_palette(0, r, g, b);
return (0);
}
return (1);
}
static struct builtin builtin_background =
{
"background",
"background RRGGBB",
"Sets the background color when in graphics mode."
"RR is red, GG is green, and BB blue. Numbers must be in hexadecimal."
};
#endif /* SUPPORT_GRAPHICS */
/* clear */
static int
{
if (current_term->cls)
current_term->cls();
return 0;
}
static struct builtin builtin_clear =
{
"clear",
"clear",
"Clear the screen"
};
/* displayapm */
static int
{
{
grub_printf ("APM BIOS information:\n"
" Version: 0x%x\n"
" 32-bit CS: 0x%x\n"
" Offset: 0x%x\n"
" 16-bit CS: 0x%x\n"
" 16-bit DS: 0x%x\n"
" 32-bit CS length: 0x%x\n"
" 16-bit CS length: 0x%x\n"
" 16-bit DS length: 0x%x\n",
(unsigned) apm_bios_info.version,
(unsigned) apm_bios_info.cseg,
(unsigned) apm_bios_info.cseg_16,
(unsigned) apm_bios_info.dseg_16,
(unsigned) apm_bios_info.cseg_len,
(unsigned) apm_bios_info.cseg_16_len,
(unsigned) apm_bios_info.dseg_16_len);
}
else
{
grub_printf ("No APM BIOS found or probe failed\n");
}
return 0;
}
static struct builtin builtin_displayapm =
{
"displayapm",
"displayapm",
"Display APM BIOS information."
};
/* displaymem */
static int
{
if (get_eisamemsize () != -1)
grub_printf (" EISA Memory BIOS Interface is present\n");
if (get_mmap_entry ((void *) SCRATCHADDR, 0) != 0
|| *((int *) SCRATCHADDR) != 0)
grub_printf (" Address Map BIOS Interface is present\n");
grub_printf (" Lower memory: %uK, "
"Upper memory (to first chipset hole): %uK\n",
{
grub_printf (" [Address Range Descriptor entries "
"immediately follow (values are 64-bit)]\n");
{
char *str;
str = "Usable RAM";
else
str = "Reserved";
grub_printf (" %s: Base Address: 0x%x X 4GB + 0x%x,\n"
" Length: 0x%x X 4GB + 0x%x bytes\n",
str,
}
}
return 0;
}
static struct builtin builtin_displaymem =
{
"displaymem",
"displaymem",
"Display what GRUB thinks the system address space map of the"
" machine is, including all regions of physical RAM installed."
};
/* dump FROM TO */
#ifdef GRUB_UTIL
static int
{
char c;
{
return 1;
}
nul_terminate (to);
return 1;
if (! fp)
{
return 1;
}
while (grub_read (&c, 1))
{
return 1;
}
{
return 1;
}
grub_close ();
return 0;
}
static struct builtin builtin_dump =
{
"dump",
"dump FROM TO",
"Dump the contents of the file FROM to the file TO. FROM must be"
" a GRUB file and TO must be an OS file."
};
#endif /* GRUB_UTIL */
static char embed_info[32];
/* embed */
/* Embed a Stage 1.5 in the first cylinder after MBR or in the
bootloader block in a FFS. */
static int
{
char *stage1_5;
char *device;
int sector;
/* Open a Stage 1.5. */
return 1;
/* Read the whole of the Stage 1.5. */
grub_close ();
if (errnum)
return 1;
/* Get the device where the Stage 1.5 will be embedded. */
set_device (device);
if (errnum)
return 1;
if (current_partition == 0xFFFFFF)
{
/* Embed it after the MBR. */
char mbr[SECTOR_SIZE];
int i;
/* Open the partition. */
if (! open_partition ())
return 1;
/* No floppy has MBR. */
if (! (current_drive & 0x80))
{
return 1;
}
/* Read the MBR of CURRENT_DRIVE. */
return 1;
/* Sanity check. */
if (! PC_MBR_CHECK_SIG (mbr))
{
return 1;
}
/* Check if the disk can store the Stage 1.5. */
for (i = 0; i < 4; i++)
{
return 1;
}
/* Check for EZ-BIOS signature. It should be in the third
* sector, but due to remapping it can appear in the second, so
* load and check both.
*/
return 1;
{
/* The space after the MBR is used by EZ-BIOS which we must
* not overwrite.
*/
return 1;
}
sector = 1;
}
else
{
/* Embed it in the bootloader block in the filesystem. */
int start_sector;
/* Open the partition. */
if (! open_device ())
return 1;
/* Check if the current slice supports embedding. */
{
return 1;
}
}
/* Clear the cache. */
buf_track = -1;
/* Now perform the embedding. */
return 1;
return 0;
}
static struct builtin builtin_embed =
{
"embed",
"embed STAGE1_5 DEVICE",
"Embed the Stage 1.5 STAGE1_5 in the sectors after MBR if DEVICE"
" is a drive, or in the \"bootloader\" area if DEVICE is a FFS partition."
" Print the number of sectors which STAGE1_5 occupies if successful."
};
/* fallback */
static int
{
int i = 0;
while (*arg)
{
int entry;
int j;
return 1;
/* Remove duplications to prevent infinite looping. */
for (j = 0; j < i; j++)
if (entry == fallback_entries[j])
break;
if (j != i)
continue;
fallback_entries[i++] = entry;
if (i == MAX_FALLBACK_ENTRIES)
break;
}
if (i < MAX_FALLBACK_ENTRIES)
fallback_entries[i] = -1;
fallback_entryno = (i == 0) ? -1 : 0;
return 0;
}
static struct builtin builtin_fallback =
{
"fallback",
#if 0
"fallback NUM...",
"Go into unattended boot mode: if the default boot entry has any"
" errors, instead of waiting for the user to do anything, it"
" immediately starts over using the NUM entry (same numbering as the"
" `default' command). This obviously won't help if the machine"
" was rebooted by a kernel that GRUB loaded."
#endif
};
/* find */
/* Search for the filename ARG in all of partitions. */
static int
{
unsigned long drive;
unsigned long tmp_drive = saved_drive;
unsigned long tmp_partition = saved_partition;
int got_file = 0;
/* Floppies. */
{
current_partition = 0xFFFFFF;
if (open_device ())
{
{
grub_close ();
got_file = 1;
}
}
}
/* Hard disks. */
{
unsigned long part = 0xFFFFFF;
char buf[SECTOR_SIZE];
&ext_offset, buf))
{
if (type != PC_SLICE_TYPE_NONE
&& ! IS_PC_SLICE_TYPE_BSD (type)
&& ! IS_PC_SLICE_TYPE_EXTENDED (type))
{
if (open_device ())
{
{
grub_close ();
if (bsd_part == 0xFF)
grub_printf (" (hd%d,%d)\n",
else
grub_printf (" (hd%d,%d,%c)\n",
got_file = 1;
}
}
}
/* We want to ignore any error here. */
}
/* next_partition always sets ERRNUM in the last call, so clear
it. */
}
if (got_file)
{
return 0;
}
return 1;
}
static struct builtin builtin_find =
{
"find",
"find FILENAME",
"Search for the filename FILENAME in all of partitions and print the list of"
" the devices which contain the file."
};
/* fstest */
static int
{
if (disk_read_hook)
{
printf (" Filesystem tracing is now off\n");
}
else
{
printf (" Filesystem tracing is now on\n");
}
return 0;
}
static struct builtin builtin_fstest =
{
"fstest",
"fstest",
"Toggle filesystem test mode."
};
/* geometry */
static int
{
char *msg;
#ifdef GRUB_UTIL
char *ptr;
#endif
/* Get the device number. */
set_device (device);
if (errnum)
return 1;
/* Check for the geometry. */
{
return 1;
}
/* Attempt to read the first sector, because some BIOSes turns out not
to support LBA even though they set the bit 0 in the support
bitmap, only after reading something actually. */
{
return 1;
}
#ifdef GRUB_UTIL
if (*ptr)
{
return 1;
else
errnum = 0;
buf_drive = -1;
}
#endif /* GRUB_UTIL */
#ifdef GRUB_UTIL
#else
msg = "LBA";
else
msg = "CHS";
#endif
grub_printf ("drive 0x%x: C/H/S = %d/%d/%d, "
"The number of sectors = %d, %s\n",
real_open_partition (1);
return 0;
}
static struct builtin builtin_geometry =
{
"geometry",
"geometry DRIVE [CYLINDER HEAD SECTOR [TOTAL_SECTOR]]",
"Print the information for a drive DRIVE. In the grub shell, you can"
" set the geometry of the drive arbitrarily. The number of the cylinders,"
" the one of the heads, the one of the sectors and the one of the total"
" sectors are set to CYLINDER, HEAD, SECTOR and TOTAL_SECTOR,"
" respectively. If you omit TOTAL_SECTOR, then it will be calculated based"
" on the C/H/S values automatically."
};
/* halt */
static int
{
int no_apm;
/* Never reach here. */
return 1;
}
static struct builtin builtin_halt =
{
"halt",
"halt [--no-apm]",
"Halt your system. If APM is avaiable on it, turn off the power using"
" the APM BIOS, unless you specify the option `--no-apm'."
};
/* help */
#define MAX_SHORT_DOC_LEN 39
#define MAX_LONG_DOC_LEN 66
static int
{
int all = 0;
{
all = 1;
}
if (! *arg)
{
/* Invoked with no argument. Print the list of the short docs. */
int left = 1;
{
int len;
int i;
/* If this cannot be used in the command-line interface,
skip this. */
continue;
/* If this doesn't need to be listed automatically and "--all"
is not specified, skip this. */
continue;
/* If the length of SHORT_DOC is too long, truncate it. */
for (i = 0; i < len; i++)
for (; i < MAX_SHORT_DOC_LEN; i++)
grub_putchar (' ');
if (! left)
grub_putchar ('\n');
}
/* If the last entry was at the left column, no newline was printed
at the end. */
if (! left)
grub_putchar ('\n');
}
else
{
/* Invoked with one or more patterns. */
do
{
char *next_arg;
/* Get the next argument. */
/* Terminate ARG. */
nul_terminate (arg);
{
/* Skip this if this is only for the configuration file. */
continue;
{
/* At first, print the name and the short doc. */
grub_printf ("%s: %s\n",
/* Print the long doc. */
while (*doc)
{
int i;
/* If LEN is too long, fold DOC. */
if (len > MAX_LONG_DOC_LEN)
{
/* Fold this line at the position of a space. */
break;
}
grub_printf (" ");
for (i = 0; i < len; i++)
grub_putchar (*doc++);
grub_putchar ('\n');
}
}
}
}
while (*arg);
}
return 0;
}
static struct builtin builtin_help =
{
"help",
"help [--all] [PATTERN ...]",
"Display helpful information about builtin commands. Not all commands"
" aren't shown without the option `--all'."
};
/* hiddenmenu */
static int
{
show_menu = 0;
return 0;
}
static struct builtin builtin_hiddenmenu =
{
"hiddenmenu",
#if 0
"hiddenmenu",
"Hide the menu."
#endif
};
/* hide */
static int
{
if (! set_device (arg))
return 1;
if (! set_partition_hidden_flag (1))
return 1;
return 0;
}
static struct builtin builtin_hide =
{
"hide",
"hide PARTITION",
"Hide PARTITION by setting the \"hidden\" bit in"
" its partition type code."
};
#ifdef SUPPORT_NETBOOT
/* ifconfig */
static int
{
if (! grub_eth_probe ())
{
grub_printf ("No ethernet card found.\n");
return 1;
}
while (*arg)
{
else
{
return 1;
}
}
{
return 1;
}
return 0;
}
static struct builtin builtin_ifconfig =
{
"ifconfig",
"ifconfig [--address=IP] [--gateway=IP] [--mask=MASK] [--server=IP]",
"Configure the IP address, the netmask, the gateway and the server"
" address or print current network configuration."
};
#endif /* SUPPORT_NETBOOT */
/* impsprobe */
static int
{
#ifdef GRUB_UTIL
/* In the grub shell, we cannot probe IMPS. */
return 1;
#else /* ! GRUB_UTIL */
if (!imps_probe ())
printf (" No MPS information found or probe failed\n");
return 0;
#endif /* ! GRUB_UTIL */
}
static struct builtin builtin_impsprobe =
{
"impsprobe",
"impsprobe",
"Probe the Intel Multiprocessor Specification 1.1 or 1.4"
" configuration table and boot the various CPUs which are found into"
" a tight loop."
};
/* initrd */
static int
{
switch (kernel_type)
{
case KERNEL_TYPE_LINUX:
case KERNEL_TYPE_BIG_LINUX:
if (! load_initrd (arg))
return 1;
break;
default:
return 1;
}
return 0;
}
static struct builtin builtin_initrd =
{
"initrd",
"initrd FILE [ARG ...]",
"Load an initial ramdisk FILE for a Linux format boot image and set the"
" appropriate parameters in the Linux setup area in memory."
};
/* install */
static int
{
/* XXX: Probably SECTOR_SIZE is reasonable. */
int new_drive = GRUB_INVALID_DRIVE;
int i;
int saved_sector;
char *ptr;
int installaddr, installlist;
/* Point to the location of the name of a configuration file in Stage 2. */
char *config_file_location;
/* If FILE is a Stage 1.5? */
int is_stage1_5 = 0;
/* Must call grub_close? */
int is_open = 0;
/* If LBA is forced? */
int is_force_lba = 0;
/* Was the last sector full? */
int last_length = SECTOR_SIZE;
#ifdef GRUB_UTIL
/* If the Stage 2 is in a partition mounted by an OS, this will store
the filename under the OS. */
char *stage2_os_file = 0;
#endif /* GRUB_UTIL */
/* Save the first sector of Stage2 in STAGE2_SECT. */
{
if (debug)
/* ReiserFS has files which sometimes contain data not aligned
on sector boundaries. Returning an error is better than
silently failing. */
}
/* Write SECTOR to INSTALLLIST, and update INSTALLADDR and
INSTALLSECT. */
{
if (debug)
{
/* We found a non-sector-aligned data block. */
return;
}
if (*((unsigned long *) (installlist - 4))
+ *((unsigned short *) installlist) != sector
{
installlist -= 8;
if (*((unsigned long *) (installlist - 8)))
else
{
}
}
*((unsigned short *) installlist) += 1;
installaddr += 512;
}
/* First, check the GNU-style long option. */
while (1)
{
{
is_force_lba = 1;
}
#ifdef GRUB_UTIL
{
}
#endif /* GRUB_UTIL */
else
break;
}
stage1_file = arg;
if (*dest_dev == 'd')
{
new_drive = 0;
}
/* Get the installation address. */
{
/* ADDR is not specified. */
installaddr = 0;
errnum = 0;
}
else
#ifndef NO_DECOMPRESSION
/* Do not decompress Stage 1 or Stage 2. */
no_decompression = 1;
#endif
/* Read Stage 1. */
if (! is_open
goto fail;
/* Read the old sector from DEST_DEV. */
if (! set_device (dest_dev)
|| ! open_partition ()
goto fail;
/* Store the information for the destination device. */
/* Copy the possible DOS BPB, 59 bytes at byte offset 3. */
if (dest_drive & 0x80)
/* Check for the version and the signature of Stage 1. */
|| (*((unsigned short *) (stage1_buffer + BOOTSEC_SIG_OFFSET))
!= BOOTSEC_SIGNATURE))
{
goto fail;
}
/* This below is not true any longer. But should we leave this alone? */
/* If DEST_DRIVE is a floppy, Stage 2 must have the iteration probe
routine. */
if (! (dest_drive & 0x80)
|| stage1_buffer[BOOTSEC_PART_OFFSET] == 0))
{
goto fail;
}
grub_close ();
/* Open Stage 2. */
if (! is_open)
goto fail;
if (! new_drive)
else if (src_drive != dest_drive)
grub_printf ("Warning: the option `d' was not used, but the Stage 1 will"
" be installed on a\ndifferent drive than the drive where"
" the Stage 2 resides.\n");
/* Set the boot drive. */
/* Set the "force LBA" flag. */
/* Set the boot drive mask. This is a workaround for buggy BIOSes which
don't pass boot drive correctly. Instead, they pass 0x00 even when
booted from 0x80. */
*((unsigned char *) (stage1_buffer + STAGE1_BOOT_DRIVE_MASK))
= (dest_drive & BIOS_FLAG_FIXED_DISK);
/* Read the first sector of Stage 2. */
goto fail;
/* Read the second sector of Stage 2. */
goto fail;
/* Check for the version of Stage 2. */
if (*((short *) (stage2_second_buffer + STAGE2_VER_MAJ_OFFS))
!= COMPAT_VERSION)
{
goto fail;
}
/* Check for the Stage 2 id. */
is_stage1_5 = 1;
/* If INSTALLADDR is not specified explicitly in the command-line,
determine it by the Stage 2 id. */
if (! installaddr)
{
if (! is_stage1_5)
/* Stage 2. */
installaddr = 0x8000;
else
/* Stage 1.5. */
installaddr = 0x2000;
}
*((unsigned long *) (stage1_buffer + STAGE1_STAGE2_SECTOR))
*((unsigned short *) (stage1_buffer + STAGE1_STAGE2_ADDRESS))
= installaddr;
*((unsigned short *) (stage1_buffer + STAGE1_STAGE2_SEGMENT))
= installaddr >> 4;
while (*((unsigned long *) i))
{
if (i < (int) stage2_first_buffer
|| (*((int *) (i - 4)) & 0x80000000)
|| *((unsigned short *) i) >= 0xA00
|| *((short *) (i + 2)) == 0)
{
goto fail;
}
*((int *) i) = 0;
*((int *) (i - 4)) = 0;
i -= 8;
}
/* Read the whole of Stage2 except for the first sector. */
goto fail;
disk_read_hook = 0;
/* Find a string for the configuration filename. */
while (*(config_file_location++))
;
/* Set the "force LBA" flag for Stage2. */
*((unsigned char *) (stage2_second_buffer + STAGE2_FORCE_LBA))
= is_force_lba;
if (*ptr == 'p')
{
*((long *) (stage2_second_buffer + STAGE2_INSTALLPART))
if (is_stage1_5)
{
/* Reset the device information in FILE if it is a Stage 1.5. */
unsigned long device = 0xFFFFFFFF;
sizeof (device));
}
}
if (*ptr)
{
if (! is_stage1_5)
/* If it is a Stage 2, just copy PTR to CONFIG_FILE_LOCATION. */
else
{
char *real_config;
unsigned long device;
/* Translate the external device syntax to the internal device
syntax. */
{
/* The Stage 2 PTR does not contain the device name, so
use the root device instead. */
real_config = ptr;
}
if (current_drive == src_drive)
{
/* If the drive where the Stage 2 resides is the same as
the one where the Stage 1.5 resides, do not embed the
drive number. */
}
sizeof (device));
}
/* If a Stage 1.5 is used, then we need to modify the Stage2. */
if (is_stage1_5)
{
if (! is_open)
goto fail;
/* Skip the first sector. */
goto fail;
disk_read_hook = 0;
grub_close ();
is_open = 0;
/* Sanity check. */
{
goto fail;
}
/* Set the "force LBA" flag for Stage2. */
/* If REAL_CONFIG_FILENAME is specified, copy it to the Stage2. */
if (*real_config_filename)
{
/* Specified */
char *location;
/* Find a string for the configuration filename. */
while (*(location++))
;
/* Copy the name. */
}
/* Write it to the disk. */
buf_track = -1;
#ifdef GRUB_UTIL
/* In the grub shell, access the Stage 2 via the OS filesystem
service, if possible. */
if (stage2_os_file)
{
if (! fp)
{
goto fail;
}
{
goto fail;
}
!= SECTOR_SIZE)
{
goto fail;
}
}
else
#endif /* GRUB_UTIL */
{
goto fail;
}
}
}
/* Clear the cache. */
buf_track = -1;
/* Write the modified sectors of Stage2 to the disk. */
#ifdef GRUB_UTIL
if (! is_stage1_5 && stage2_os_file)
{
if (! fp)
{
goto fail;
}
{
goto fail;
}
{
goto fail;
}
}
else
#endif /* GRUB_UTIL */
{
/* The first. */
if (! open_partition ())
goto fail;
goto fail;
goto fail;
}
/* Write the modified sector of Stage 1 to the disk. */
if (! open_partition ())
goto fail;
fail:
if (is_open)
grub_close ();
disk_read_hook = 0;
#ifndef NO_DECOMPRESSION
no_decompression = 0;
#endif
return errnum;
}
static struct builtin builtin_install =
{
"install",
"install [--stage2=STAGE2_FILE] [--force-lba] STAGE1 [d] DEVICE STAGE2 [ADDR] [p] [CONFIG_FILE] [REAL_CONFIG_FILE]",
"Install STAGE1 on DEVICE, and install a blocklist for loading STAGE2"
" as a Stage 2. If the option `d' is present, the Stage 1 will always"
" look for the disk where STAGE2 was installed, rather than using"
" the booting drive. The Stage 2 will be loaded at address ADDR, which"
" will be determined automatically if you don't specify it. If"
" the option `p' or CONFIG_FILE is present, then the first block"
" of Stage 2 is patched with new values of the partition and name"
" of the configuration file used by the true Stage 2 (for a Stage 1.5,"
" this is the name of the true Stage 2) at boot time. If STAGE2 is a Stage"
" 1.5 and REAL_CONFIG_FILE is present, then the Stage 2 CONFIG_FILE is"
" patched with the configuration filename REAL_CONFIG_FILE."
" If the option `--force-lba' is specified, disable some sanity checks"
" for LBA mode. If the option `--stage2' is specified, rewrite the Stage"
" 2 via your OS's filesystem instead of the raw device."
};
/* ioprobe */
static int
{
#ifdef GRUB_UTIL
return 1;
#else /* ! GRUB_UTIL */
unsigned short *port;
/* Get the drive number. */
set_device (arg);
if (errnum)
return 1;
/* Clean out IO_MAP. */
/* Track the int13 handler. */
/* Print out the result. */
return 0;
#endif /* ! GRUB_UTIL */
}
static struct builtin builtin_ioprobe =
{
"ioprobe",
"ioprobe DRIVE",
"Probe I/O ports used for the drive DRIVE."
};
/* kernel */
static int
{
int len;
unsigned long load_flags = 0;
#ifndef AUTO_LINUX_MEM_OPT
#endif
/* Deal with GNU-style long options. */
while (1)
{
/* If the option `--type=TYPE' is specified, convert the string to
a kernel type. */
{
arg += 7;
/* XXX: For now, OpenBSD is identical to NetBSD, from GRUB's
point of view. */
else
{
return 1;
}
}
/* If the `--no-mem-option' is specified, don't pass a Linux's mem
option automatically. If the kernel is another type, this flag
has no effect. */
else
break;
/* Try the next. */
}
/* Reset MB_CMDLINE. */
mb_cmdline = (char *) MB_CMDLINE_BUF;
{
return 1;
}
/* Copy the command-line to MB_CMDLINE. */
if (kernel_type == KERNEL_TYPE_NONE)
return 1;
return 0;
}
static struct builtin builtin_kernel =
{
"kernel",
"kernel [--no-mem-option] [--type=TYPE] FILE [ARG ...]",
"Attempt to load the primary boot image from FILE. The rest of the"
" line is passed verbatim as the \"kernel command line\". Any modules"
" must be reloaded after using this command. The option --type is used"
" to suggest what type of kernel to be loaded. TYPE must be either of"
" \"netbsd\", \"freebsd\", \"openbsd\", \"linux\", \"biglinux\" and"
" \"multiboot\". The option --no-mem-option tells GRUB not to pass a"
" Linux's mem option automatically."
};
/* lock */
static int
{
{
return 1;
}
return 0;
}
static struct builtin builtin_lock =
{
"lock",
"lock",
"Break a command execution unless the user is authenticated."
};
/* makeactive */
static int
{
if (! make_saved_active ())
return 1;
return 0;
}
static struct builtin builtin_makeactive =
{
"makeactive",
"makeactive",
"Set the active partition on the root disk to GRUB's root device."
" This command is limited to _primary_ PC partitions on a hard disk."
};
/* map */
/* Map FROM_DRIVE to TO_DRIVE. */
static int
{
char *to_drive;
char *from_drive;
int i;
/* Get the drive number for TO_DRIVE. */
if (errnum)
return 1;
to = current_drive;
/* Get the drive number for FROM_DRIVE. */
if (errnum)
return 1;
/* Search for an empty slot in BIOS_DRIVE_MAP. */
for (i = 0; i < DRIVE_MAP_SIZE; i++)
{
/* Perhaps the user wants to override the map. */
break;
if (! bios_drive_map[i])
break;
}
if (i == DRIVE_MAP_SIZE)
{
return 1;
}
/* If TO is equal to FROM, delete the entry. */
sizeof (unsigned short) * (DRIVE_MAP_SIZE - i));
else
return 0;
}
static struct builtin builtin_map =
{
"map",
"map TO_DRIVE FROM_DRIVE",
"Map the drive FROM_DRIVE to the drive TO_DRIVE. This is necessary"
" when you chain-load some operating systems, such as DOS, if such an"
" OS resides at a non-first drive."
};
#ifdef USE_MD5_PASSWORDS
/* md5crypt */
static int
{
char crypted[36];
char key[32];
unsigned int seed;
int i;
const char *const seedchars =
"./0123456789ABCDEFGHIJKLMNOPQRST"
"UVWXYZabcdefghijklmnopqrstuvwxyz";
/* First create a salt. */
/* The magical prefix. */
/* Create the length of a salt. */
/* Generate a salt. */
for (i = 0; i < 8 && seed; i++)
{
/* FIXME: This should be more random. */
seed >>= 6;
}
/* A salt must be terminated with `$', if it is less than 8 chars. */
#ifdef DEBUG_MD5CRYPT
#endif
/* Get a password. */
/* Crypt the key. */
return 0;
}
static struct builtin builtin_md5crypt =
{
"md5crypt",
"md5crypt",
"Generate a password in MD5 format."
};
#endif /* USE_MD5_PASSWORDS */
/* module */
static int
{
switch (kernel_type)
{
case KERNEL_TYPE_MULTIBOOT:
{
return 1;
}
return 1;
break;
case KERNEL_TYPE_LINUX:
case KERNEL_TYPE_BIG_LINUX:
if (! load_initrd (arg))
return 1;
break;
default:
return 1;
}
return 0;
}
static struct builtin builtin_module =
{
"module",
"module FILE [ARG ...]",
"Load a boot module FILE for a Multiboot format boot image (no"
" interpretation of the file contents is made, so users of this"
" command must know what the kernel in question expects). The"
" rest of the line is passed as the \"module command line\", like"
" the `kernel' command."
};
/* modulenounzip */
static int
{
int ret;
#ifndef NO_DECOMPRESSION
no_decompression = 1;
#endif
#ifndef NO_DECOMPRESSION
no_decompression = 0;
#endif
return ret;
}
static struct builtin builtin_modulenounzip =
{
"modulenounzip",
"modulenounzip FILE [ARG ...]",
"The same as `module', except that automatic decompression is"
" disabled."
};
/* pager [on|off] */
static int
{
/* If ARG is empty, toggle the flag. */
if (! *arg)
use_pager = 1;
use_pager = 0;
else
{
return 1;
}
return 0;
}
static struct builtin builtin_pager =
{
"pager",
"pager [FLAG]",
"Toggle pager mode with no argument. If FLAG is given and its value"
" is `on', turn on the mode. If FLAG is `off', turn off the mode."
};
/* partnew PART TYPE START LEN */
static int
{
int entry;
char mbr[512];
/* Convert a LBA address to a CHS address in the INT 13 format. */
{
}
/* Get the drive and the partition. */
if (! set_device (arg))
return 1;
/* The drive must be a hard disk. */
if (! (current_drive & 0x80))
{
return 1;
}
/* The partition must a primary partition. */
{
return 1;
}
/* Get the new partition type. */
return 1;
/* The partition type is unsigned char. */
if (new_type > 0xFF)
{
return 1;
}
/* Get the new partition start. */
return 1;
/* Get the new partition length. */
return 1;
/* Read the MBR. */
return 1;
/* Check if the new partition will fit in the disk. */
{
return 1;
}
/* Store the partition information in the MBR. */
/* Make sure that the MBR has a valid signature. */
/* Write back the MBR to the disk. */
buf_track = -1;
return 1;
return 0;
}
static struct builtin builtin_partnew =
{
"partnew",
"partnew PART TYPE START LEN",
"Create a primary partition at the starting address START with the"
" length LEN, with the type TYPE. START and LEN are in sector units."
};
/* parttype PART TYPE */
static int
{
int new_type;
unsigned long part = 0xFFFFFF;
char mbr[512];
/* Get the drive and the partition. */
if (! set_device (arg))
return 1;
/* The drive must be a hard disk. */
if (! (current_drive & 0x80))
{
return 1;
}
/* The partition must be a PC slice. */
{
return 1;
}
/* Get the new partition type. */
return 1;
/* The partition type is unsigned char. */
if (new_type > 0xFF)
{
return 1;
}
/* Look for the partition. */
&ext_offset, mbr))
{
if (part == current_partition)
{
/* Found. */
/* Set the type to NEW_TYPE. */
/* Write back the MBR to the disk. */
buf_track = -1;
return 1;
/* Succeed. */
return 0;
}
}
/* The partition was not found. ERRNUM was set by next_partition. */
return 1;
}
static struct builtin builtin_parttype =
{
"parttype",
"parttype PART TYPE",
"Change the type of the partition PART to TYPE."
};
/* password */
static int
{
int len;
#ifdef USE_MD5_PASSWORDS
{
type = PASSWORD_MD5;
}
#endif
{
}
{
/* Do password check! */
char entered[32];
/* Wipe out any previously entered password */
entered[0] = 0;
nul_terminate (arg);
{
return 1;
}
}
else
{
/* PASSWORD NUL NUL ... */
{
return 1;
}
/* Copy the password and clear the rest of the buffer. */
password = (char *) PASSWORD_BUF;
}
return 0;
}
static struct builtin builtin_password =
{
"password",
"password [--md5] PASSWD [FILE]",
"If used in the first section of a menu file, disable all"
" interactive editing control (menu entry editor and"
" command line). If the password PASSWD is entered, it loads the"
" FILE as a new config file and restarts the GRUB Stage 2. If you"
" omit the argument FILE, then GRUB just unlocks privileged"
" instructions. You can also use it in the script section, in"
" which case it will ask for the password, before continueing."
" The option --md5 tells GRUB that PASSWD is encrypted with"
" md5crypt."
};
/* pause */
static int
{
/* If ESC is returned, then abort this entry. */
return 1;
return 0;
}
static struct builtin builtin_pause =
{
"pause",
"pause [MESSAGE ...]",
"Print MESSAGE, then wait until a key is pressed."
};
#ifdef GRUB_UTIL
/* quit */
static int
{
stop ();
/* Never reach here. */
return 0;
}
static struct builtin builtin_quit =
{
"quit",
"quit",
"Exit from the GRUB shell."
};
#endif /* GRUB_UTIL */
#ifdef SUPPORT_NETBOOT
/* rarp */
static int
{
if (! rarp ())
{
return 1;
}
/* Notify the configuration. */
return 0;
}
static struct builtin builtin_rarp =
{
"rarp",
"rarp",
"Initialize a network device via RARP."
};
#endif /* SUPPORT_NETBOOT */
static int
{
int addr;
return 1;
grub_printf ("Address 0x%x: Value 0x%x\n",
return 0;
}
static struct builtin builtin_read =
{
"read",
"read ADDR",
"Read a 32-bit value from memory at address ADDR and"
" display it in hex format."
};
/* reboot */
static int
{
grub_reboot ();
/* Never reach here. */
return 1;
}
static struct builtin builtin_reboot =
{
"reboot",
"reboot",
"Reboot your system."
};
/* Print the root device information. */
static void
print_root_device (void)
{
if (saved_drive == NETWORK_DRIVE)
{
/* Network drive. */
grub_printf (" (nd):");
}
else if (saved_drive & 0x80)
{
/* Hard disk drive. */
grub_printf ("):");
}
else
{
/* Floppy disk drive. */
}
/* Print the filesystem information. */
print_fsys_type ();
}
static int
{
int hdbias = 0;
char *biasptr;
char *next;
/* If ARG is empty, just print the current root device. */
if (! *arg)
{
return 0;
}
/* Call set_device to get the drive and the partition in ARG. */
if (! next)
return 1;
/* Ignore ERR_FSYS_MOUNT. */
if (attempt_mount)
{
return 1;
}
else
{
/* This is necessary, because the location of a partition table
must be set appropriately. */
if (open_partition ())
{
set_bootdev (0);
if (errnum)
return 1;
}
}
/* Clear ERRNUM. */
errnum = 0;
if (attempt_mount)
{
/* BSD and chainloading evil hacks !! */
errnum = 0;
if (errnum)
return 1;
/* Print the type of the filesystem. */
print_fsys_type ();
}
return 0;
}
static int
{
}
static struct builtin builtin_root =
{
"root",
"root [DEVICE [HDBIAS]]",
"Set the current \"root device\" to the device DEVICE, then"
" attempt to mount it to get the partition size (for passing the"
" partition descriptor in `ES:ESI', used by some chain-loaded"
" bootloaders), the BSD drive-type (for booting BSD kernels using"
" their native boot format), and correctly determine "
" the PC partition where a BSD sub-partition is located. The"
" optional HDBIAS parameter is a number to tell a BSD kernel"
" how many BIOS drive numbers are on controllers before the current"
" one. For example, if there is an IDE disk and a SCSI disk, and your"
" FreeBSD root partition is on the SCSI disk, then use a `1' for HDBIAS."
};
/* rootnoverify */
static int
{
return real_root_func (arg, 0);
}
static struct builtin builtin_rootnoverify =
{
"rootnoverify",
"rootnoverify [DEVICE [HDBIAS]]",
"Similar to `root', but don't attempt to mount the partition. This"
" is useful for when an OS is outside of the area of the disk that"
" GRUB can read, but setting the correct root device is still"
" desired. Note that the items mentioned in `root' which"
" derived from attempting the mount will NOT work correctly."
};
/* savedefault */
static int
{
#if !defined(SUPPORT_DISKLESS) && !defined(GRUB_UTIL)
unsigned long tmp_drive = saved_drive;
unsigned long tmp_partition = saved_partition;
char *default_file = (char *) DEFAULT_FILE_BUF;
char buf[10];
char sect[SECTOR_SIZE];
int entryno;
int sector_count = 0;
int saved_sectors[2];
int saved_offsets[2];
int saved_lengths[2];
/* Save sector information about at most two sectors. */
{
if (sector_count < 2)
{
}
sector_count++;
}
/* This command is only useful when you boot an entry from the menu
interface. */
if (! (flags & BUILTIN_SCRIPT))
{
return 1;
}
/* Determine a saved entry number. */
if (*arg)
{
{
int i;
int index = 0;
for (i = 0; i < MAX_FALLBACK_ENTRIES; i++)
{
if (fallback_entries[i] < 0)
break;
if (fallback_entries[i] == current_entryno)
{
index = i + 1;
break;
}
}
{
/* This is the last. */
return 1;
}
}
return 1;
}
else
/* Open the default file. */
if (grub_open (default_file))
{
int len;
disk_read_hook = 0;
grub_close ();
{
/* This is too small. Do not modify the file manually, please! */
goto fail;
}
if (sector_count > 2)
{
/* Is this possible?! Too fragmented! */
goto fail;
}
/* Set up a string to be written. */
if (saved_lengths[0] < sizeof (buf))
{
/* The file is anchored to another file and the first few bytes
are spanned in two sectors. Uggh... */
sect))
goto fail;
goto fail;
sect))
goto fail;
buf + saved_lengths[0],
sizeof (buf) - saved_lengths[0]);
goto fail;
}
else
{
/* This is a simple case. It fits into a single sector. */
sect))
goto fail;
goto fail;
}
/* Clear the cache. */
buf_track = -1;
}
fail:
return errnum;
#else /* ! SUPPORT_DISKLESS && ! GRUB_UTIL */
return 1;
#endif /* ! SUPPORT_DISKLESS && ! GRUB_UTIL */
}
static struct builtin builtin_savedefault =
{
"savedefault",
"savedefault [NUM | `fallback']",
"Save the current entry as the default boot entry if no argument is"
" specified. If a number is specified, this number is saved. If"
" `fallback' is used, next fallback entry is saved."
};
#ifdef SUPPORT_SERIAL
/* serial */
static int
{
unsigned short port = serial_hw_get_port (0);
unsigned int speed = 9600;
int word_len = UART_8BITS_WORD;
int parity = UART_NO_PARITY;
int stop_bit_len = UART_1_STOP_BIT;
/* Process GNU-style long options.
FIXME: We should implement a getopt-like function, to avoid
duplications. */
while (1)
{
{
int unit;
if (! safe_parse_maxint (&p, &unit))
return 1;
{
return 1;
}
}
{
int num;
if (! safe_parse_maxint (&p, &num))
return 1;
}
{
int num;
if (! safe_parse_maxint (&p, &num))
return 1;
}
{
int len;
if (! safe_parse_maxint (&p, &len))
return 1;
switch (len)
{
default:
return 1;
}
}
{
int len;
if (! safe_parse_maxint (&p, &len))
return 1;
switch (len)
{
default:
return 1;
}
}
{
else
{
return 1;
}
}
# ifdef GRUB_UTIL
/* In the grub shell, don't use any port number but open a tty
device instead. */
{
char *q = dev;
while (*p && ! grub_isspace (*p))
*q++ = *p++;
*q = 0;
}
# endif /* GRUB_UTIL */
else
break;
}
/* Initialize the serial unit. */
{
return 1;
}
return 0;
}
static struct builtin builtin_serial =
{
"serial",
"serial [--unit=UNIT] [--port=PORT] [--speed=SPEED] [--word=WORD] [--parity=PARITY] [--stop=STOP] [--device=DEV]",
"Initialize a serial device. UNIT is a digit that specifies which serial"
" device is used (e.g. 0 == COM1). If you need to specify the port number,"
" set it by --port. SPEED is the DTE-DTE speed. WORD is the word length,"
" PARITY is the type of parity, which is one of `no', `odd' and `even'."
" STOP is the length of stop bit(s). The option --device can be used only"
" in the grub shell, which specifies the file name of a tty device. The"
" default values are COM1, 9600, 8N1."
};
#endif /* SUPPORT_SERIAL */
/* setkey */
struct keysym
{
char *unshifted_name; /* the name in unshifted state */
char *shifted_name; /* the name in shifted state */
unsigned char unshifted_ascii; /* the ascii code in unshifted state */
unsigned char shifted_ascii; /* the ascii code in shifted state */
unsigned char keycode; /* keyboard scancode */
};
/* The table for key symbols. If the "shifted" member of an entry is
NULL, the entry does not have shifted state. */
static struct keysym keysym_table[] =
{
{"escape", 0, 0x1b, 0, 0x01},
{"1", "exclam", '1', '!', 0x02},
{"2", "at", '2', '@', 0x03},
{"3", "numbersign", '3', '#', 0x04},
{"4", "dollar", '4', '$', 0x05},
{"5", "percent", '5', '%', 0x06},
{"6", "caret", '6', '^', 0x07},
{"7", "ampersand", '7', '&', 0x08},
{"8", "asterisk", '8', '*', 0x09},
{"9", "parenleft", '9', '(', 0x0a},
{"0", "parenright", '0', ')', 0x0b},
{"minus", "underscore", '-', '_', 0x0c},
{"equal", "plus", '=', '+', 0x0d},
{"backspace", 0, '\b', 0, 0x0e},
{"tab", 0, '\t', 0, 0x0f},
{"q", "Q", 'q', 'Q', 0x10},
{"w", "W", 'w', 'W', 0x11},
{"e", "E", 'e', 'E', 0x12},
{"r", "R", 'r', 'R', 0x13},
{"t", "T", 't', 'T', 0x14},
{"y", "Y", 'y', 'Y', 0x15},
{"u", "U", 'u', 'U', 0x16},
{"i", "I", 'i', 'I', 0x17},
{"o", "O", 'o', 'O', 0x18},
{"p", "P", 'p', 'P', 0x19},
{"bracketleft", "braceleft", '[', '{', 0x1a},
{"bracketright", "braceright", ']', '}', 0x1b},
{"enter", 0, '\n', 0, 0x1c},
{"control", 0, 0, 0, 0x1d},
{"a", "A", 'a', 'A', 0x1e},
{"s", "S", 's', 'S', 0x1f},
{"d", "D", 'd', 'D', 0x20},
{"f", "F", 'f', 'F', 0x21},
{"g", "G", 'g', 'G', 0x22},
{"h", "H", 'h', 'H', 0x23},
{"j", "J", 'j', 'J', 0x24},
{"k", "K", 'k', 'K', 0x25},
{"l", "L", 'l', 'L', 0x26},
{"semicolon", "colon", ';', ':', 0x27},
{"quote", "doublequote", '\'', '"', 0x28},
{"backquote", "tilde", '`', '~', 0x29},
{"shift", 0, 0, 0, 0x2a},
{"backslash", "bar", '\\', '|', 0x2b},
{"z", "Z", 'z', 'Z', 0x2c},
{"x", "X", 'x', 'X', 0x2d},
{"c", "C", 'c', 'C', 0x2e},
{"v", "V", 'v', 'V', 0x2f},
{"b", "B", 'b', 'B', 0x30},
{"n", "N", 'n', 'N', 0x31},
{"m", "M", 'm', 'M', 0x32},
{"comma", "less", ',', '<', 0x33},
{"period", "greater", '.', '>', 0x34},
{"slash", "question", '/', '?', 0x35},
{"alt", 0, 0, 0, 0x38},
{"space", 0, ' ', 0, 0x39},
{"capslock", 0, 0, 0, 0x3a},
{"F1", 0, 0, 0, 0x3b},
{"F2", 0, 0, 0, 0x3c},
{"F3", 0, 0, 0, 0x3d},
{"F4", 0, 0, 0, 0x3e},
{"F5", 0, 0, 0, 0x3f},
{"F6", 0, 0, 0, 0x40},
{"F7", 0, 0, 0, 0x41},
{"F8", 0, 0, 0, 0x42},
{"F9", 0, 0, 0, 0x43},
{"F10", 0, 0, 0, 0x44},
/* Caution: do not add NumLock here! we cannot deal with it properly. */
{"delete", 0, 0x7f, 0, 0x53}
};
static int
{
int map_in_interrupt = 0;
static int find_key_code (char *key)
{
int i;
for (i = 0; i < sizeof (keysym_table) / sizeof (keysym_table[0]); i++)
{
if (keysym_table[i].unshifted_name &&
return keysym_table[i].keycode;
else if (keysym_table[i].shifted_name &&
return keysym_table[i].keycode;
}
return 0;
}
static int find_ascii_code (char *key)
{
int i;
for (i = 0; i < sizeof (keysym_table) / sizeof (keysym_table[0]); i++)
{
if (keysym_table[i].unshifted_name &&
return keysym_table[i].unshifted_ascii;
else if (keysym_table[i].shifted_name &&
return keysym_table[i].shifted_ascii;
}
return 0;
}
if (! *to_key)
{
/* If the user specifies no argument, reset the key mappings. */
return 0;
}
else if (! *from_key)
{
/* The user must specify two arguments or zero argument. */
return 1;
}
{
map_in_interrupt = 1;
{
return 1;
}
}
if (map_in_interrupt)
{
int i;
/* Find an empty slot. */
for (i = 0; i < KEY_MAP_SIZE; i++)
{
/* Perhaps the user wants to overwrite the map. */
break;
if (! bios_key_map[i])
break;
}
if (i == KEY_MAP_SIZE)
{
return 1;
}
/* If TO is equal to FROM, delete the entry. */
grub_memmove ((char *) &bios_key_map[i],
(char *) &bios_key_map[i + 1],
sizeof (unsigned short) * (KEY_MAP_SIZE - i));
else
/* Ugly but should work. */
}
else
{
int i;
/* Find an empty slot. */
for (i = 0; i < KEY_MAP_SIZE; i++)
{
/* Perhaps the user wants to overwrite the map. */
break;
if (! ascii_key_map[i])
break;
}
if (i == KEY_MAP_SIZE)
{
return 1;
}
/* If TO is equal to FROM, delete the entry. */
grub_memmove ((char *) &ascii_key_map[i],
(char *) &ascii_key_map[i + 1],
sizeof (unsigned short) * (KEY_MAP_SIZE - i));
else
}
return 0;
}
static struct builtin builtin_setkey =
{
"setkey",
"setkey [TO_KEY FROM_KEY]",
"Change the keyboard map. The key FROM_KEY is mapped to the key TO_KEY."
" A key must be an alphabet, a digit, or one of these: escape, exclam,"
" at, numbersign, dollar, percent, caret, ampersand, asterisk, parenleft,"
" parenright, minus, underscore, equal, plus, backspace, tab, bracketleft,"
" braceleft, bracketright, braceright, enter, control, semicolon, colon,"
" quote, doublequote, backquote, tilde, shift, backslash, bar, comma,"
" less, period, greater, slash, question, alt, space, capslock, FX (X"
" is a digit), and delete. If no argument is specified, reset key"
" mappings."
};
/* setup */
static int
{
char *install_ptr;
reside. */
char *image_ptr;
unsigned long installed_drive, installed_partition;
unsigned long image_drive, image_partition;
unsigned long tmp_drive, tmp_partition;
char stage1[64];
char stage2[64];
char config_filename[64];
char real_config_filename[64];
char cmd_arg[256];
char device[16];
int is_force_lba = 0;
char *stage2_arg = 0;
char *prefix = 0;
auto int check_file (char *file);
/* Check if the file FILE exists like Autoconf. */
int check_file (char *file)
{
int ret;
if (ret)
{
grub_close ();
grub_printf ("yes\n");
}
else
grub_printf ("no\n");
return ret;
}
/* Construct a device name in DEVICE. */
{
drive & ~0x80);
{
char tmp[16];
}
{
char tmp[16];
}
}
{
/* We install GRUB into the MBR, so try to embed the
Stage 1.5 in the sectors right after the MBR. */
/* Notify what will be run. */
if (! errnum)
{
/* Construct the blocklist representation. */
grub_printf ("succeeded\n");
return 1;
}
else
{
grub_printf ("failed (this is not fatal)\n");
return 0;
}
}
struct stage1_5_map {
char *fsys;
char *name;
};
struct stage1_5_map stage1_5_map[] =
{
{"ext2fs", "/e2fs_stage1_5"},
{"fat", "/fat_stage1_5"},
{"ufs2", "/ufs2_stage1_5"},
{"ffs", "/ffs_stage1_5"},
{"iso9660", "/iso9660_stage1_5"},
{"jfs", "/jfs_stage1_5"},
{"minix", "/minix_stage1_5"},
{"reiserfs", "/reiserfs_stage1_5"},
{"vstafs", "/vstafs_stage1_5"},
{"xfs", "/xfs_stage1_5"},
{"ufs", "/ufs_stage1_5"}
};
/* Check if the user specifies --force-lba. */
while (1)
{
{
is_force_lba = 1;
}
{
}
#ifdef GRUB_UTIL
{
stage2_arg = arg;
}
#endif /* GRUB_UTIL */
else
break;
}
install_ptr = arg;
/* Make sure that INSTALL_PTR is valid. */
if (errnum)
return 1;
/* Mount the drive pointed by IMAGE_PTR. */
if (*image_ptr)
{
get the drive and the partition. */
if (errnum)
return 1;
}
else
{
/* If omitted, use SAVED_PARTITION and SAVED_DRIVE. */
}
/* Open it. */
if (! open_device ())
goto fail;
/* Check if stage1 exists. If the user doesn't specify the option
/* NOTE: It is dangerous to run this command without `--prefix' in the
grub shell, since that affects `--stage2'. */
if (! prefix)
{
if (! check_file (stage1))
{
prefix = "/grub";
if (! check_file (stage1))
goto fail;
}
}
else
{
if (! check_file (stage1))
goto fail;
}
/* The prefix was determined. */
*real_config_filename = 0;
/* Check if stage2 exists. */
if (! check_file (stage2))
goto fail;
{
int i;
/* Iterate finding the same filesystem name as FSYS. */
for (i = 0; i < size; i++)
{
/* OK, check if the Stage 1.5 exists. */
char stage1_5[64];
if (check_file (stage1_5))
{
if (embed_stage1_5 (stage1_5,
|| embed_stage1_5 (stage1_5,
{
}
}
errnum = 0;
break;
}
}
/* Construct a string that is used by the command "install" as its
arguments. */
#if 1
/* Don't embed a drive number unnecessarily. */
#else /* NOT USED */
/* This code was used, because we belived some BIOSes had a problem
that they didn't pass a booting drive correctly. It turned out,
however, stage1 could trash a booting drive when checking LBA support,
because some BIOSes modified the register %dx in INT 13H, AH=48H.
So it becamed unclear whether GRUB should use a pre-defined booting
drive or not. If the problem still exists, it would be necessary to
switch back to this code. */
#endif /* NOT USED */
/* Notify what will be run. */
/* Make sure that SAVED_DRIVE and SAVED_PARTITION are identical
with IMAGE_DRIVE and IMAGE_PARTITION, respectively. */
/* Run the command. */
grub_printf ("succeeded\nDone.\n");
else
grub_printf ("failed\n");
fail:
return errnum;
}
static struct builtin builtin_setup =
{
"setup",
"setup [--prefix=DIR] [--stage2=STAGE2_FILE] [--force-lba] INSTALL_DEVICE [IMAGE_DEVICE]",
"Set up the installation of GRUB automatically. This command uses"
" the more flexible command \"install\" in the backend and installs"
" GRUB into the device INSTALL_DEVICE. If IMAGE_DEVICE is specified,"
" then find the GRUB images in the device IMAGE_DEVICE, otherwise"
" use the current \"root device\", which can be set by the command"
" \"root\". If you know that your BIOS should support LBA but GRUB"
" doesn't work in LBA mode, specify the option `--force-lba'."
" If you install GRUB under the grub shell and you cannot unmount the"
" partition where GRUB images reside, specify the option `--stage2'"
" to tell GRUB the file name under your OS."
};
/* terminal */
static int
{
/* The index of the default terminal in TERM_TABLE. */
int default_term = -1;
int to = -1;
int lines = 0;
int no_message = 0;
unsigned long term_flags = 0;
/* XXX: Assume less than 32 terminals. */
unsigned long term_bitmap = 0;
/* Get GNU-style long options. */
while (1)
{
term_flags |= TERM_DUMB;
/* ``--no-echo'' implies ``--no-edit''. */
{
return 1;
}
{
return 1;
/* Probably less than four is meaningless.... */
if (lines < 4)
{
return 1;
}
}
no_message = 1;
else
break;
}
/* If no argument is specified, show current setting. */
if (! *arg)
{
grub_printf ("%s%s%s%s\n",
return 0;
}
while (*arg)
{
int i;
nul_terminate (arg);
for (i = 0; term_table[i].name; i++)
{
{
{
return 1;
}
if (default_term < 0)
default_term = i;
term_bitmap |= (1 << i);
break;
}
}
if (! term_table[i].name)
{
return 1;
}
}
/* If multiple terminals are specified, wait until the user pushes any
key on one of the terminals. */
{
/* XXX: Disable the pager. */
count_lines = -1;
/* Get current time. */
;
/* Wait for a key input. */
while (to)
{
int i;
for (i = 0; term_table[i].name; i++)
{
if (term_bitmap & (1 << i))
{
if (term_table[i].checkkey () >= 0)
{
(void) term_table[i].getkey ();
default_term = i;
goto end;
}
}
}
/* Prompt the user, once per sec. */
{
if (! no_message)
{
/* Need to set CURRENT_TERM to each of selected
terminals. */
for (i = 0; term_table[i].name; i++)
if (term_bitmap & (1 << i))
{
current_term = term_table + i;
grub_printf ("\rPress any key to continue.\n");
}
/* Restore CURRENT_TERM. */
}
if (to > 0)
to--;
}
}
}
end:
if (lines)
else
/* If the interface is currently the command-line,
restart it to repaint the screen. */
if (current_term->startup)
current_term->startup();
}
return 0;
}
static struct builtin builtin_terminal =
{
"terminal",
"terminal [--dumb] [--no-echo] [--no-edit] [--timeout=SECS] [--lines=LINES] [--silent] [console] [serial] [hercules] [graphics]",
"Select a terminal. When multiple terminals are specified, wait until"
" you push any key to continue. If both console and serial are specified,"
" the terminal to which you input a key first will be selected. If no"
" argument is specified, print current setting. The option --dumb"
" specifies that your terminal is dumb, otherwise, vt100-compatibility"
" is assumed. If you specify --no-echo, input characters won't be echoed."
" If you specify --no-edit, the BASH-like editing feature will be disabled."
" If --timeout is present, this command will wait at most for SECS"
" seconds. The option --lines specifies the maximum number of lines."
" The option --silent is used to suppress messages."
};
#endif /* SUPPORT_SERIAL || SUPPORT_HERCULES || SUPPORT_GRAPHICS */
#ifdef SUPPORT_SERIAL
static int
{
if (*arg)
{
struct
{
const char *name;
char *var;
}
options[] =
{
};
while (*arg)
{
int i;
nul_terminate (arg);
{
{
break;
}
}
{
return errnum;
}
}
{
return errnum;
}
ti_set_term (&term);
}
else
{
/* No option specifies printing out current settings. */
ti_get_term (&term);
grub_printf ("name=%s\n",
grub_printf ("cursor_address=%s\n",
grub_printf ("clear_screen=%s\n",
grub_printf ("enter_standout_mode=%s\n",
grub_printf ("exit_standout_mode=%s\n",
}
return 0;
}
static struct builtin builtin_terminfo =
{
"terminfo",
"terminfo [--name=NAME --cursor-address=SEQ [--clear-screen=SEQ]"
" [--enter-standout-mode=SEQ] [--exit-standout-mode=SEQ]]",
"Define the capabilities of your terminal. Use this command to"
" define escape sequences, if it is not vt100-compatible."
" You may use \\e for ESC and ^X for a control character."
" If no option is specified, the current settings are printed."
};
#endif /* SUPPORT_SERIAL */
/* testload */
static int
{
int i;
return 1;
/* Perform filesystem test on the specified file. */
/* Read whole file first. */
grub_printf ("Whole file: ");
/* Now compare two sections of the file read differently. */
for (i = 0; i < 0x10ac0; i++)
{
*((unsigned char *) RAW_ADDR (0x200000 + i)) = 0;
}
/* First partial read. */
grub_printf ("\nPartial read 1: ");
grub_seek (0);
/* Second partial read. */
grub_printf ("\nPartial read 2: ");
grub_seek (0);
grub_printf ("\nHeader1 = 0x%x, next = 0x%x, next = 0x%x, next = 0x%x\n",
*((int *) RAW_ADDR (0x200000)),
*((int *) RAW_ADDR (0x200004)),
*((int *) RAW_ADDR (0x200008)),
*((int *) RAW_ADDR (0x20000c)));
grub_printf ("Header2 = 0x%x, next = 0x%x, next = 0x%x, next = 0x%x\n",
*((int *) RAW_ADDR (0x300000)),
*((int *) RAW_ADDR (0x300004)),
*((int *) RAW_ADDR (0x300008)),
*((int *) RAW_ADDR (0x30000c)));
for (i = 0; i < 0x10ac0; i++)
if (*((unsigned char *) RAW_ADDR (0x200000 + i))
!= *((unsigned char *) RAW_ADDR (0x300000 + i)))
break;
disk_read_hook = 0;
grub_close ();
return 0;
}
static struct builtin builtin_testload =
{
"testload",
"testload FILE",
"Read the entire contents of FILE in several different ways and"
" compares them, to test the filesystem code. The output is somewhat"
" cryptic, but if no errors are reported and the final `i=X,"
" filepos=Y' reading has X and Y equal, then it is definitely"
" consistent, and very likely works correctly subject to a"
" consistent offset error. If this test succeeds, then a good next"
" step is to try loading a kernel."
};
/* testvbe MODE */
static int
{
int mode_number;
struct vbe_controller controller;
if (! *arg)
{
return 1;
}
return 1;
/* Preset `VBE2'. */
/* Detect VBE BIOS. */
{
grub_printf (" VBE BIOS is not present.\n");
return 0;
}
{
grub_printf (" VBE version %d.%d is not supported.\n",
return 0;
}
{
return 0;
}
/* Now trip to the graphics mode. */
{
return 0;
}
/* Draw something on the screen... */
{
/* FIXME: this assumes that any depth is a modulo of 8. */
int x, y;
unsigned color = 0;
/* Iterate drawing on the screen, until the user hits any key. */
while (checkkey () == -1)
{
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
int i;
}
color++;
}
}
/* Discard the input. */
getkey ();
}
/* Back to the default text mode. */
{
/* Why?! */
grub_reboot ();
}
return 0;
}
static struct builtin builtin_testvbe =
{
"testvbe",
"testvbe MODE",
"Test the VBE mode MODE. Hit any key to return."
};
#ifdef SUPPORT_NETBOOT
/* tftpserver */
static int
{
{
return 1;
}
return 0;
}
static struct builtin builtin_tftpserver =
{
"tftpserver",
"tftpserver IPADDR",
"Override the TFTP server address."
};
#endif /* SUPPORT_NETBOOT */
/* timeout */
static int
{
return 1;
return 0;
}
static struct builtin builtin_timeout =
{
"timeout",
#if 0
"timeout SEC",
"Set a timeout, in SEC seconds, before automatically booting the"
" default entry (normally the first entry defined)."
#endif
};
/* title */
static int
{
/* This function is not actually used at least currently. */
return 0;
}
static struct builtin builtin_title =
{
"title",
#if 0
"title [NAME ...]",
"Start a new boot entry, and set its name to the contents of the"
" rest of the line, starting with the first non-space character."
#endif
};
/* unhide */
static int
{
if (! set_device (arg))
return 1;
if (! set_partition_hidden_flag (0))
return 1;
return 0;
}
static struct builtin builtin_unhide =
{
"unhide",
"unhide PARTITION",
"Unhide PARTITION by clearing the \"hidden\" bit in its"
" partition type code."
};
/* uppermem */
static int
{
return 1;
return 0;
}
static struct builtin builtin_uppermem =
{
"uppermem",
"uppermem KBYTES",
"Force GRUB to assume that only KBYTES kilobytes of upper memory are"
" installed. Any system address range maps are discarded."
};
/* vbeprobe */
static int
{
struct vbe_controller controller;
unsigned short *mode_list;
int mode_number = -1;
auto unsigned long vbe_far_ptr_to_linear (unsigned long);
unsigned long vbe_far_ptr_to_linear (unsigned long ptr)
{
}
if (*arg)
{
return 1;
}
/* Set the signature to `VBE2', to obtain VBE 3.0 information. */
{
grub_printf (" VBE BIOS is not present.\n");
return 0;
}
/* Check the version. */
{
grub_printf (" VBE version %d.%d is not supported.\n",
return 0;
}
/* Print some information. */
grub_printf (" VBE version %d.%d\n",
/* Iterate probing modes. */
for (mode_list
*mode_list != 0xFFFF;
mode_list++)
{
continue;
/* Skip this, if this is not supported or linear frame buffer
mode is not support. */
continue;
{
char *model;
switch (mode.memory_model)
{
default: model = "Unknown"; break;
}
grub_printf (" 0x%x: %s, %ux%ux%u\n",
(unsigned) *mode_list,
(unsigned) mode.x_resolution,
(unsigned) mode.y_resolution,
(unsigned) mode.bits_per_pixel);
if (mode_number != -1)
break;
}
}
return 0;
}
static struct builtin builtin_vbeprobe =
{
"vbeprobe",
"vbeprobe [MODE]",
"Probe VBE information. If the mode number MODE is specified, show only"
" the information about only the mode."
};
/* The table of builtin commands. Sorted in dictionary order. */
struct builtin *builtin_table[] =
{
#ifdef SUPPORT_GRAPHICS
#endif
#ifdef SUPPORT_NETBOOT
#endif /* SUPPORT_NETBOOT */
#ifdef GRUB_UTIL
#endif /* GRUB_UTIL */
#ifdef SUPPORT_NETBOOT
#endif /* SUPPORT_NETBOOT */
#ifdef GRUB_UTIL
#endif /* GRUB_UTIL */
#ifdef SUPPORT_GRAPHICS
#endif
#ifdef SUPPORT_NETBOOT
#endif /* SUPPORT_NETBOOT */
#ifdef USE_MD5_PASSWORDS
#endif /* USE_MD5_PASSWORDS */
#if defined(RPC_DEBUG) && defined(SUPPORT_NETBOOT)
#endif /* RPC_DEBUG && SUPPORT_NETBOOT */
#ifdef GRUB_UTIL
#endif /* GRUB_UTIL */
#ifdef SUPPORT_NETBOOT
#endif /* SUPPORT_NETBOOT */
#ifdef SUPPORT_SERIAL
#endif /* SUPPORT_SERIAL */
#ifdef SUPPORT_GRAPHICS
#endif /* SUPPORT_GRAPHICS */
#endif /* SUPPORT_SERIAL || SUPPORT_HERCULES || SUPPORT_GRAPHICS */
#ifdef SUPPORT_SERIAL
#endif /* SUPPORT_SERIAL */
#ifdef SUPPORT_NETBOOT
#endif /* SUPPORT_NETBOOT */
0
};