mirror of
https://git.savannah.gnu.org/git/grub.git
synced 2026-10-22 07:12:39 +00:00
From Sergey Matveychuk <sem@ciam.ru>: * stage2/size_test: Added a check for ufs2_stage1_5. * stage2/shared.h (STAGE2_ID_UFS2_STAGE1_5): New macro. [FSYS_UFS2] (STAGE2_ID): Set to STAGE2_ID_UFS2_STAGE1_5. * stage2/filesys.h (FSYS_UFS2_NUM): New macro. [FSYS_UFS2] (ufs2_mount): New prototype. [FSYS_UFS2] (ufs2_read): Likewise. [FSYS_UFS2] (ufs2_dir): Likewise. [FSYS_UFS2] (ufs2_embed): Likewise. (NUM_FSYS): Added FSYS_UFS2_NUM. * stage2/disk_io.c (fsys_table): Added an ufs2 entry. * stage2/builtins.c (setup_func): Added ufs2 into the STAGE1_5_MAP. * stage2/Makefile.am (libgrub_a_SOURCES): Added fsys_ufs2.c. (libgrub_a_CFLAGS): Added -DFSYS_UFS2=1. (pkgdata_DATA): Added ufs2_stage1_5. (noinst_PROGRAMS): Added ufs2_stage1_5.exec. (ufs2_stage1_5_exec_SOURCES): New variable. (ufs2_stage1_5_exec_CFLAGS): Likewise. (ufs2_stage1_5_exec_CCASFLAGS): Likewise. (ufs2_stage1_5_exec_LDFLAGS): Likewise. * grub/Makefile.am (AM_CPPFLAGS): Added -DFSYS_ISO9660=1, -DFSYS_JFS=1, -DFSYS_REISERFS=1, -DFSYS_UFS2=1, -DFSYS_VSTAFS=1, -DFSYS_XFS=1, and -DUSE_MD5_PASSWORDS=1. * configure.ac (--disable-ufs2): New option. * stage2/fsys_ufs2.c: New file. * stage2/ufs2.h: Likewise.
4758 lines
111 KiB
C
4758 lines
111 KiB
C
/* builtins.c - the GRUB builtin commands */
|
|
/*
|
|
* GRUB -- GRand Unified Bootloader
|
|
* Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* 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.
|
|
*/
|
|
|
|
/* Include stdio.h before shared.h, because we can't define
|
|
WITHOUT_LIBC_STUBS here. */
|
|
#ifdef GRUB_UTIL
|
|
# include <stdio.h>
|
|
#endif
|
|
|
|
#include <shared.h>
|
|
#include <filesys.h>
|
|
#include <term.h>
|
|
|
|
#ifdef SUPPORT_NETBOOT
|
|
# define GRUB 1
|
|
# include <etherboot.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. */
|
|
kernel_t kernel_type;
|
|
/* 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_entry = -1;
|
|
/* 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. */
|
|
password_t 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. */
|
|
static unsigned short bios_drive_map[DRIVE_MAP_SIZE + 1];
|
|
|
|
/* Prototypes for allowing straightfoward calling of builtins functions
|
|
inside other functions. */
|
|
static int configfile_func (char *arg, int flags);
|
|
|
|
/* Initialize the data for builtins. */
|
|
void
|
|
init_builtins (void)
|
|
{
|
|
kernel_type = KERNEL_TYPE_NONE;
|
|
/* 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_entry = -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
|
|
check_password (char *entered, char* expected, password_t type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case PASSWORD_PLAIN:
|
|
return strcmp (entered, expected);
|
|
|
|
#ifdef USE_MD5_PASSWORDS
|
|
case PASSWORD_MD5:
|
|
return check_md5_password (entered, expected);
|
|
#endif
|
|
default:
|
|
/* unsupported password type: be secure */
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* Print which sector is read when loading a file. */
|
|
static void
|
|
disk_read_print_func (int sector, int offset, int length)
|
|
{
|
|
grub_printf ("[%d,%d,%d]", sector, offset, length);
|
|
}
|
|
|
|
|
|
/* blocklist */
|
|
static int
|
|
blocklist_func (char *arg, int flags)
|
|
{
|
|
char *dummy = (char *) RAW_ADDR (0x100000);
|
|
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. */
|
|
static void disk_read_blocklist_func (int sector, int offset, int length)
|
|
{
|
|
if (num_sectors > 0)
|
|
{
|
|
if (start_sector + num_sectors == sector
|
|
&& offset == 0 && last_length == SECTOR_SIZE)
|
|
{
|
|
num_sectors++;
|
|
last_length = length;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (last_length == SECTOR_SIZE)
|
|
grub_printf ("%s%d+%d", num_entries ? "," : "",
|
|
start_sector - part_start, num_sectors);
|
|
else if (num_sectors > 1)
|
|
grub_printf ("%s%d+%d,%d[0-%d]", num_entries ? "," : "",
|
|
start_sector - part_start, num_sectors-1,
|
|
start_sector + num_sectors-1 - part_start,
|
|
last_length);
|
|
else
|
|
grub_printf ("%s%d[0-%d]", num_entries ? "," : "",
|
|
start_sector - part_start, last_length);
|
|
num_entries++;
|
|
num_sectors = 0;
|
|
}
|
|
}
|
|
|
|
if (offset > 0)
|
|
{
|
|
grub_printf("%s%d[%d-%d]", num_entries ? "," : "",
|
|
sector-part_start, offset, offset+length);
|
|
num_entries++;
|
|
}
|
|
else
|
|
{
|
|
start_sector = sector;
|
|
num_sectors = 1;
|
|
last_length = length;
|
|
}
|
|
}
|
|
|
|
/* Open the file. */
|
|
if (! grub_open (arg))
|
|
return 1;
|
|
|
|
/* Print the device name. */
|
|
grub_printf ("(%cd%d",
|
|
(current_drive & 0x80) ? 'h' : 'f',
|
|
current_drive & ~0x80);
|
|
|
|
if ((current_partition & 0xFF0000) != 0xFF0000)
|
|
grub_printf (",%d", (current_partition >> 16) & 0xFF);
|
|
|
|
if ((current_partition & 0x00FF00) != 0x00FF00)
|
|
grub_printf (",%c", 'a' + ((current_partition >> 8) & 0xFF));
|
|
|
|
grub_printf (")");
|
|
|
|
/* Read in the whole file to DUMMY. */
|
|
disk_read_hook = disk_read_blocklist_func;
|
|
if (! grub_read (dummy, -1))
|
|
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 ("%s%d+%d", num_entries ? "," : "",
|
|
start_sector - part_start, num_sectors);
|
|
|
|
grub_printf ("\n");
|
|
|
|
fail:
|
|
disk_read_hook = 0;
|
|
grub_close ();
|
|
return errnum;
|
|
}
|
|
|
|
static struct builtin builtin_blocklist =
|
|
{
|
|
"blocklist",
|
|
blocklist_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"blocklist FILE",
|
|
"Print the blocklist notation of the file FILE."
|
|
};
|
|
|
|
/* boot */
|
|
static int
|
|
boot_func (char *arg, int flags)
|
|
{
|
|
/* 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)
|
|
unset_int15_handler ();
|
|
|
|
#ifdef SUPPORT_NETBOOT
|
|
/* Shut down the networking. */
|
|
cleanup_net ();
|
|
#endif
|
|
|
|
switch (kernel_type)
|
|
{
|
|
case KERNEL_TYPE_FREEBSD:
|
|
case KERNEL_TYPE_NETBSD:
|
|
/* *BSD */
|
|
bsd_boot (kernel_type, bootdev, (char *) mbi.cmdline);
|
|
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;
|
|
else if ((bios_drive_map[i] & 0xFF) == saved_drive)
|
|
{
|
|
/* Exchage SAVED_DRIVE with the mapped drive. */
|
|
saved_drive = (bios_drive_map[i] >> 8) & 0xFF;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Set the handler. This is somewhat dangerous. */
|
|
set_int13_handler (bios_drive_map);
|
|
}
|
|
|
|
gateA20 (0);
|
|
boot_drive = saved_drive;
|
|
chain_stage1 (0, BOOTSEC_LOCATION, boot_part_addr);
|
|
break;
|
|
|
|
case KERNEL_TYPE_MULTIBOOT:
|
|
/* Multiboot */
|
|
multi_boot ((int) entry_addr, (int) &mbi);
|
|
break;
|
|
|
|
default:
|
|
errnum = ERR_BOOT_COMMAND;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_boot =
|
|
{
|
|
"boot",
|
|
boot_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"boot",
|
|
"Boot the OS/chain-loader which has been loaded."
|
|
};
|
|
|
|
|
|
#ifdef SUPPORT_NETBOOT
|
|
/* bootp */
|
|
static int
|
|
bootp_func (char *arg, int flags)
|
|
{
|
|
int with_configfile = 0;
|
|
|
|
if (grub_memcmp (arg, "--with-configfile", sizeof ("--with-configfile") - 1)
|
|
== 0)
|
|
{
|
|
with_configfile = 1;
|
|
arg = skip_to (0, arg);
|
|
}
|
|
|
|
if (! bootp ())
|
|
{
|
|
if (errnum == ERR_NONE)
|
|
errnum = ERR_DEV_VALUES;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Notify the configuration. */
|
|
print_network_configuration ();
|
|
|
|
/* XXX: this can cause an endless loop, but there is no easy way to
|
|
detect such a loop unfortunately. */
|
|
if (with_configfile)
|
|
configfile_func (config_file, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_bootp =
|
|
{
|
|
"bootp",
|
|
bootp_func,
|
|
BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
|
|
"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
|
|
cat_func (char *arg, int flags)
|
|
{
|
|
char c;
|
|
|
|
if (! grub_open (arg))
|
|
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. */
|
|
if (grub_isspace (c) || (c >= ' ' && c <= '~'))
|
|
grub_putchar (c);
|
|
else
|
|
grub_putchar ('?');
|
|
}
|
|
|
|
grub_close ();
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_cat =
|
|
{
|
|
"cat",
|
|
cat_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"cat FILE",
|
|
"Print the contents of the file FILE."
|
|
};
|
|
|
|
|
|
/* chainloader */
|
|
static int
|
|
chainloader_func (char *arg, int flags)
|
|
{
|
|
int force = 0;
|
|
char *file = arg;
|
|
|
|
/* If the option `--force' is specified? */
|
|
if (substring ("--force", arg) <= 0)
|
|
{
|
|
force = 1;
|
|
file = skip_to (0, arg);
|
|
}
|
|
|
|
/* Open the file. */
|
|
if (! grub_open (file))
|
|
{
|
|
kernel_type = KERNEL_TYPE_NONE;
|
|
return 1;
|
|
}
|
|
|
|
/* Read the first block. */
|
|
if (grub_read ((char *) BOOTSEC_LOCATION, SECTOR_SIZE) != SECTOR_SIZE)
|
|
{
|
|
grub_close ();
|
|
kernel_type = KERNEL_TYPE_NONE;
|
|
|
|
/* This below happens, if a file whose size is less than 512 bytes
|
|
is loaded. */
|
|
if (errnum == ERR_NONE)
|
|
errnum = ERR_EXEC_FORMAT;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* If not loading it forcibly, check for the signature. */
|
|
if (! force
|
|
&& (*((unsigned short *) (BOOTSEC_LOCATION + BOOTSEC_SIG_OFFSET))
|
|
!= BOOTSEC_SIGNATURE))
|
|
{
|
|
grub_close ();
|
|
errnum = ERR_EXEC_FORMAT;
|
|
kernel_type = KERNEL_TYPE_NONE;
|
|
return 1;
|
|
}
|
|
|
|
grub_close ();
|
|
kernel_type = KERNEL_TYPE_CHAINLOADER;
|
|
|
|
/* XXX: Windows evil hack. For now, only the first five letters are
|
|
checked. */
|
|
if (IS_PC_SLICE_TYPE_FAT (current_slice)
|
|
&& ! grub_memcmp ((char *) BOOTSEC_LOCATION + BOOTSEC_BPB_SYSTEM_ID,
|
|
"MSWIN", 5))
|
|
*((unsigned long *) (BOOTSEC_LOCATION + BOOTSEC_BPB_HIDDEN_SECTORS))
|
|
= part_start;
|
|
|
|
errnum = ERR_NONE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_chainloader =
|
|
{
|
|
"chainloader",
|
|
chainloader_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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
|
|
cmp_func (char *arg, int flags)
|
|
{
|
|
/* The filenames. */
|
|
char *file1, *file2;
|
|
/* The addresses. */
|
|
char *addr1, *addr2;
|
|
int i;
|
|
/* The size of the file. */
|
|
int size;
|
|
|
|
/* Get the filenames from ARG. */
|
|
file1 = arg;
|
|
file2 = skip_to (0, arg);
|
|
if (! *file1 || ! *file2)
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
|
|
/* Terminate the filenames for convenience. */
|
|
nul_terminate (file1);
|
|
nul_terminate (file2);
|
|
|
|
/* Read the whole data from FILE1. */
|
|
addr1 = (char *) RAW_ADDR (0x100000);
|
|
if (! grub_open (file1))
|
|
return 1;
|
|
|
|
/* Get the size. */
|
|
size = filemax;
|
|
if (grub_read (addr1, -1) != size)
|
|
{
|
|
grub_close ();
|
|
return 1;
|
|
}
|
|
|
|
grub_close ();
|
|
|
|
/* Read the whole data from FILE2. */
|
|
addr2 = addr1 + size;
|
|
if (! grub_open (file2))
|
|
return 1;
|
|
|
|
/* Check if the size of FILE2 is equal to the one of FILE2. */
|
|
if (size != filemax)
|
|
{
|
|
grub_printf ("Differ in size: 0x%x [%s], 0x%x [%s]\n",
|
|
size, file1, filemax, file2);
|
|
grub_close ();
|
|
return 0;
|
|
}
|
|
|
|
if (! grub_read (addr2, -1))
|
|
{
|
|
grub_close ();
|
|
return 1;
|
|
}
|
|
|
|
grub_close ();
|
|
|
|
/* Now compare ADDR1 with ADDR2. */
|
|
for (i = 0; i < size; i++)
|
|
{
|
|
if (addr1[i] != addr2[i])
|
|
grub_printf ("Differ at the offset %d: 0x%x [%s], 0x%x [%s]\n",
|
|
i, (unsigned) addr1[i], file1,
|
|
(unsigned) addr2[i], file2);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_cmp =
|
|
{
|
|
"cmp",
|
|
cmp_func,
|
|
BUILTIN_CMDLINE,
|
|
"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
|
|
color_func (char *arg, int flags)
|
|
{
|
|
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. */
|
|
for (ptr = str; *ptr && *ptr != '/'; ptr++)
|
|
;
|
|
|
|
/* 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. */
|
|
if (substring ("blink-", str) <= 0)
|
|
{
|
|
color = 0x80;
|
|
str += 6;
|
|
}
|
|
|
|
/* Search for the color name. */
|
|
for (i = 0; i < 16; i++)
|
|
if (grub_strcmp (color_list[i], str) == 0)
|
|
{
|
|
color |= i;
|
|
break;
|
|
}
|
|
|
|
if (i == 16)
|
|
return -1;
|
|
|
|
str = ptr;
|
|
nul_terminate (str);
|
|
|
|
/* Search for the color name. */
|
|
for (i = 0; i < 8; i++)
|
|
if (grub_strcmp (color_list[i], str) == 0)
|
|
{
|
|
color |= i << 4;
|
|
break;
|
|
}
|
|
|
|
if (i == 8)
|
|
return -1;
|
|
|
|
return color;
|
|
}
|
|
|
|
normal = arg;
|
|
highlight = skip_to (0, arg);
|
|
|
|
new_normal_color = color_number (normal);
|
|
if (new_normal_color < 0 && ! safe_parse_maxint (&normal, &new_normal_color))
|
|
return 1;
|
|
|
|
/* The second argument is optional, so set highlight_color
|
|
to inverted NORMAL_COLOR. */
|
|
if (! *highlight)
|
|
new_highlight_color = ((new_normal_color >> 4)
|
|
| ((new_normal_color & 0xf) << 4));
|
|
else
|
|
{
|
|
new_highlight_color = color_number (highlight);
|
|
if (new_highlight_color < 0
|
|
&& ! safe_parse_maxint (&highlight, &new_highlight_color))
|
|
return 1;
|
|
}
|
|
|
|
if (current_term->setcolor)
|
|
current_term->setcolor (new_normal_color, new_highlight_color);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_color =
|
|
{
|
|
"color",
|
|
color_func,
|
|
BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
|
|
"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."
|
|
" The format of a color is \"FG/BG\". FG and BG are symbolic color names."
|
|
" 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
|
|
configfile_func (char *arg, int flags)
|
|
{
|
|
char *new_config = config_file;
|
|
|
|
/* Check if the file ARG is present. */
|
|
if (! grub_open (arg))
|
|
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_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"configfile FILE",
|
|
"Load FILE as the configuration file."
|
|
};
|
|
|
|
|
|
/* debug */
|
|
static int
|
|
debug_func (char *arg, int flags)
|
|
{
|
|
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_func,
|
|
BUILTIN_CMDLINE,
|
|
"debug",
|
|
"Turn on/off the debug mode."
|
|
};
|
|
|
|
|
|
/* default */
|
|
static int
|
|
default_func (char *arg, int flags)
|
|
{
|
|
#ifndef SUPPORT_DISKLESS
|
|
if (grub_strcmp (arg, "saved") == 0)
|
|
{
|
|
default_entry = saved_entryno;
|
|
return 0;
|
|
}
|
|
#endif /* SUPPORT_DISKLESS */
|
|
|
|
if (! safe_parse_maxint (&arg, &default_entry))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_default =
|
|
{
|
|
"default",
|
|
default_func,
|
|
BUILTIN_MENU,
|
|
#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
|
|
device_func (char *arg, int flags)
|
|
{
|
|
char *drive = arg;
|
|
char *device;
|
|
|
|
/* Get the drive number from DRIVE. */
|
|
if (! set_device (drive))
|
|
return 1;
|
|
|
|
/* Get the device argument. */
|
|
device = skip_to (0, drive);
|
|
|
|
/* Terminate DEVICE. */
|
|
nul_terminate (device);
|
|
|
|
if (! *device || ! check_device (device))
|
|
{
|
|
errnum = ERR_FILE_NOT_FOUND;
|
|
return 1;
|
|
}
|
|
|
|
assign_device_name (current_drive, device);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_device =
|
|
{
|
|
"device",
|
|
device_func,
|
|
BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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
|
|
/* dhcp */
|
|
static int
|
|
dhcp_func (char *arg, int flags)
|
|
{
|
|
/* For now, this is an alias for bootp. */
|
|
return bootp_func (arg, flags);
|
|
}
|
|
|
|
static struct builtin builtin_dhcp =
|
|
{
|
|
"dhcp",
|
|
dhcp_func,
|
|
BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
|
|
"dhcp",
|
|
"Initialize a network device via DHCP."
|
|
};
|
|
#endif /* SUPPORT_NETBOOT */
|
|
|
|
|
|
/* displayapm */
|
|
static int
|
|
displayapm_func (char *arg, int flags)
|
|
{
|
|
if (mbi.flags & MB_INFO_APM_TABLE)
|
|
{
|
|
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,
|
|
apm_bios_info.offset,
|
|
(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_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"displayapm",
|
|
"Display APM BIOS information."
|
|
};
|
|
|
|
|
|
/* displaymem */
|
|
static int
|
|
displaymem_func (char *arg, int flags)
|
|
{
|
|
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",
|
|
mbi.mem_lower, mbi.mem_upper);
|
|
|
|
if (mbi.flags & MB_INFO_MEM_MAP)
|
|
{
|
|
struct AddrRangeDesc *map = (struct AddrRangeDesc *) mbi.mmap_addr;
|
|
int end_addr = mbi.mmap_addr + mbi.mmap_length;
|
|
|
|
grub_printf (" [Address Range Descriptor entries "
|
|
"immediately follow (values are 64-bit)]\n");
|
|
while (end_addr > (int) map)
|
|
{
|
|
char *str;
|
|
|
|
if (map->Type == MB_ARD_MEMORY)
|
|
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,
|
|
(unsigned long) (map->BaseAddr >> 32),
|
|
(unsigned long) (map->BaseAddr & 0xFFFFFFFF),
|
|
(unsigned long) (map->Length >> 32),
|
|
(unsigned long) (map->Length & 0xFFFFFFFF));
|
|
|
|
map = ((struct AddrRangeDesc *) (((int) map) + 4 + map->size));
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_displaymem =
|
|
{
|
|
"displaymem",
|
|
displaymem_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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
|
|
dump_func (char *arg, int flags)
|
|
{
|
|
char *from, *to;
|
|
FILE *fp;
|
|
char c;
|
|
|
|
from = arg;
|
|
to = skip_to (0, arg);
|
|
if (! *from || ! *to)
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
|
|
nul_terminate (from);
|
|
nul_terminate (to);
|
|
|
|
if (! grub_open (from))
|
|
return 1;
|
|
|
|
fp = fopen (to, "w");
|
|
if (! fp)
|
|
{
|
|
errnum = ERR_WRITE;
|
|
return 1;
|
|
}
|
|
|
|
while (grub_read (&c, 1))
|
|
if (fputc (c, fp) == EOF)
|
|
{
|
|
errnum = ERR_WRITE;
|
|
fclose (fp);
|
|
return 1;
|
|
}
|
|
|
|
if (fclose (fp) == EOF)
|
|
{
|
|
errnum = ERR_WRITE;
|
|
return 1;
|
|
}
|
|
|
|
grub_close ();
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_dump =
|
|
{
|
|
"dump",
|
|
dump_func,
|
|
BUILTIN_CMDLINE,
|
|
"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
|
|
embed_func (char *arg, int flags)
|
|
{
|
|
char *stage1_5;
|
|
char *device;
|
|
char *stage1_5_buffer = (char *) RAW_ADDR (0x100000);
|
|
int len, size;
|
|
int sector;
|
|
|
|
stage1_5 = arg;
|
|
device = skip_to (0, stage1_5);
|
|
|
|
/* Open a Stage 1.5. */
|
|
if (! grub_open (stage1_5))
|
|
return 1;
|
|
|
|
/* Read the whole of the Stage 1.5. */
|
|
len = grub_read (stage1_5_buffer, -1);
|
|
grub_close ();
|
|
|
|
if (errnum)
|
|
return 1;
|
|
|
|
size = (len + SECTOR_SIZE - 1) / SECTOR_SIZE;
|
|
|
|
/* 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];
|
|
char ezbios_check[2*SECTOR_SIZE];
|
|
int i;
|
|
|
|
/* Open the partition. */
|
|
if (! open_partition ())
|
|
return 1;
|
|
|
|
/* No floppy has MBR. */
|
|
if (! (current_drive & 0x80))
|
|
{
|
|
errnum = ERR_DEV_VALUES;
|
|
return 1;
|
|
}
|
|
|
|
/* Read the MBR of CURRENT_DRIVE. */
|
|
if (! rawread (current_drive, PC_MBR_SECTOR, 0, SECTOR_SIZE, mbr))
|
|
return 1;
|
|
|
|
/* Sanity check. */
|
|
if (! PC_MBR_CHECK_SIG (mbr))
|
|
{
|
|
errnum = ERR_BAD_PART_TABLE;
|
|
return 1;
|
|
}
|
|
|
|
/* Check if the disk can store the Stage 1.5. */
|
|
for (i = 0; i < 4; i++)
|
|
if (PC_SLICE_TYPE (mbr, i) && PC_SLICE_START (mbr, i) - 1 < size)
|
|
{
|
|
errnum = ERR_NO_DISK_SPACE;
|
|
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.
|
|
*/
|
|
if (! rawread (current_drive, 1, 0, 2 * SECTOR_SIZE, ezbios_check))
|
|
return 1;
|
|
|
|
if (! memcmp (ezbios_check + 3, "AERMH", 5)
|
|
|| ! memcmp (ezbios_check + 512 + 3, "AERMH", 5))
|
|
{
|
|
/* The space after the MBR is used by EZ-BIOS which we must
|
|
* not overwrite.
|
|
*/
|
|
errnum = ERR_NO_DISK_SPACE;
|
|
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. */
|
|
if (fsys_table[fsys_type].embed_func == 0
|
|
|| ! fsys_table[fsys_type].embed_func (&start_sector, size))
|
|
{
|
|
errnum = ERR_DEV_VALUES;
|
|
return 1;
|
|
}
|
|
|
|
sector = part_start + start_sector;
|
|
}
|
|
|
|
/* Clear the cache. */
|
|
buf_track = -1;
|
|
|
|
/* Now perform the embedding. */
|
|
if (! devwrite (sector - part_start, size, stage1_5_buffer))
|
|
return 1;
|
|
|
|
grub_printf (" %d sectors are embedded.\n", size);
|
|
grub_sprintf (embed_info, "%d+%d", sector - part_start, size);
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_embed =
|
|
{
|
|
"embed",
|
|
embed_func,
|
|
BUILTIN_CMDLINE,
|
|
"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
|
|
fallback_func (char *arg, int flags)
|
|
{
|
|
if (! safe_parse_maxint (&arg, &fallback_entry))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_fallback =
|
|
{
|
|
"fallback",
|
|
fallback_func,
|
|
BUILTIN_MENU,
|
|
#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
|
|
find_func (char *arg, int flags)
|
|
{
|
|
char *filename = arg;
|
|
unsigned long drive;
|
|
unsigned long tmp_drive = saved_drive;
|
|
unsigned long tmp_partition = saved_partition;
|
|
int got_file = 0;
|
|
|
|
/* Floppies. */
|
|
for (drive = 0; drive < 8; drive++)
|
|
{
|
|
current_drive = drive;
|
|
current_partition = 0xFFFFFF;
|
|
|
|
if (open_device ())
|
|
{
|
|
saved_drive = current_drive;
|
|
saved_partition = current_partition;
|
|
if (grub_open (filename))
|
|
{
|
|
grub_close ();
|
|
grub_printf (" (fd%d)\n", drive);
|
|
got_file = 1;
|
|
}
|
|
}
|
|
|
|
errnum = ERR_NONE;
|
|
}
|
|
|
|
/* Hard disks. */
|
|
for (drive = 0x80; drive < 0x88; drive++)
|
|
{
|
|
unsigned long part = 0xFFFFFF;
|
|
unsigned long start, len, offset, ext_offset;
|
|
int type, entry;
|
|
char buf[SECTOR_SIZE];
|
|
|
|
current_drive = drive;
|
|
while (next_partition (drive, 0xFFFFFF, &part, &type,
|
|
&start, &len, &offset, &entry,
|
|
&ext_offset, buf))
|
|
{
|
|
if (type != PC_SLICE_TYPE_NONE
|
|
&& ! IS_PC_SLICE_TYPE_BSD (type)
|
|
&& ! IS_PC_SLICE_TYPE_EXTENDED (type))
|
|
{
|
|
current_partition = part;
|
|
if (open_device ())
|
|
{
|
|
saved_drive = current_drive;
|
|
saved_partition = current_partition;
|
|
if (grub_open (filename))
|
|
{
|
|
int bsd_part = (part >> 8) & 0xFF;
|
|
int pc_slice = part >> 16;
|
|
|
|
grub_close ();
|
|
|
|
if (bsd_part == 0xFF)
|
|
grub_printf (" (hd%d,%d)\n",
|
|
drive - 0x80, pc_slice);
|
|
else
|
|
grub_printf (" (hd%d,%d,%c)\n",
|
|
drive - 0x80, pc_slice, bsd_part + 'a');
|
|
|
|
got_file = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We want to ignore any error here. */
|
|
errnum = ERR_NONE;
|
|
}
|
|
|
|
/* next_partition always sets ERRNUM in the last call, so clear
|
|
it. */
|
|
errnum = ERR_NONE;
|
|
}
|
|
|
|
saved_drive = tmp_drive;
|
|
saved_partition = tmp_partition;
|
|
|
|
if (got_file)
|
|
{
|
|
errnum = ERR_NONE;
|
|
return 0;
|
|
}
|
|
|
|
errnum = ERR_FILE_NOT_FOUND;
|
|
return 1;
|
|
}
|
|
|
|
static struct builtin builtin_find =
|
|
{
|
|
"find",
|
|
find_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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
|
|
fstest_func (char *arg, int flags)
|
|
{
|
|
if (disk_read_hook)
|
|
{
|
|
disk_read_hook = NULL;
|
|
printf (" Filesystem tracing is now off\n");
|
|
}
|
|
else
|
|
{
|
|
disk_read_hook = disk_read_print_func;
|
|
printf (" Filesystem tracing is now on\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_fstest =
|
|
{
|
|
"fstest",
|
|
fstest_func,
|
|
BUILTIN_CMDLINE,
|
|
"fstest",
|
|
"Toggle filesystem test mode."
|
|
};
|
|
|
|
|
|
/* geometry */
|
|
static int
|
|
geometry_func (char *arg, int flags)
|
|
{
|
|
struct geometry geom;
|
|
char *msg;
|
|
char *device = arg;
|
|
#ifdef GRUB_UTIL
|
|
char *ptr;
|
|
#endif
|
|
|
|
/* Get the device number. */
|
|
set_device (device);
|
|
if (errnum)
|
|
return 1;
|
|
|
|
/* Check for the geometry. */
|
|
if (get_diskinfo (current_drive, &geom))
|
|
{
|
|
errnum = ERR_NO_DISK;
|
|
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. */
|
|
if (biosdisk (BIOSDISK_READ, current_drive, &geom, 0, 1, SCRATCHSEG))
|
|
{
|
|
errnum = ERR_READ;
|
|
return 1;
|
|
}
|
|
|
|
#ifdef GRUB_UTIL
|
|
ptr = skip_to (0, device);
|
|
if (*ptr)
|
|
{
|
|
char *cylinder, *head, *sector, *total_sector;
|
|
int num_cylinder, num_head, num_sector, num_total_sector;
|
|
|
|
cylinder = ptr;
|
|
head = skip_to (0, cylinder);
|
|
sector = skip_to (0, head);
|
|
total_sector = skip_to (0, sector);
|
|
if (! safe_parse_maxint (&cylinder, &num_cylinder)
|
|
|| ! safe_parse_maxint (&head, &num_head)
|
|
|| ! safe_parse_maxint (§or, &num_sector))
|
|
return 1;
|
|
|
|
disks[current_drive].cylinders = num_cylinder;
|
|
disks[current_drive].heads = num_head;
|
|
disks[current_drive].sectors = num_sector;
|
|
|
|
if (safe_parse_maxint (&total_sector, &num_total_sector))
|
|
disks[current_drive].total_sectors = num_total_sector;
|
|
else
|
|
disks[current_drive].total_sectors
|
|
= num_cylinder * num_head * num_sector;
|
|
errnum = 0;
|
|
|
|
geom = disks[current_drive];
|
|
buf_drive = -1;
|
|
}
|
|
#endif /* GRUB_UTIL */
|
|
|
|
#ifdef GRUB_UTIL
|
|
msg = device_map[current_drive];
|
|
#else
|
|
if (geom.flags & BIOSDISK_FLAG_LBA_EXTENSION)
|
|
msg = "LBA";
|
|
else
|
|
msg = "CHS";
|
|
#endif
|
|
|
|
grub_printf ("drive 0x%x: C/H/S = %d/%d/%d, "
|
|
"The number of sectors = %d, %s\n",
|
|
current_drive,
|
|
geom.cylinders, geom.heads, geom.sectors,
|
|
geom.total_sectors, msg);
|
|
real_open_partition (1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_geometry =
|
|
{
|
|
"geometry",
|
|
geometry_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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
|
|
halt_func (char *arg, int flags)
|
|
{
|
|
int no_apm;
|
|
|
|
no_apm = (grub_memcmp (arg, "--no-apm", 8) == 0);
|
|
grub_halt (no_apm);
|
|
|
|
/* Never reach here. */
|
|
return 1;
|
|
}
|
|
|
|
static struct builtin builtin_halt =
|
|
{
|
|
"halt",
|
|
halt_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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
|
|
help_func (char *arg, int flags)
|
|
{
|
|
int all = 0;
|
|
|
|
if (grub_memcmp (arg, "--all", sizeof ("--all") - 1) == 0)
|
|
{
|
|
all = 1;
|
|
arg = skip_to (0, arg);
|
|
}
|
|
|
|
if (! *arg)
|
|
{
|
|
/* Invoked with no argument. Print the list of the short docs. */
|
|
struct builtin **builtin;
|
|
int left = 1;
|
|
|
|
for (builtin = builtin_table; *builtin != 0; builtin++)
|
|
{
|
|
int len;
|
|
int i;
|
|
|
|
/* If this cannot be used in the command-line interface,
|
|
skip this. */
|
|
if (! ((*builtin)->flags & BUILTIN_CMDLINE))
|
|
continue;
|
|
|
|
/* If this doesn't need to be listed automatically and "--all"
|
|
is not specified, skip this. */
|
|
if (! all && ! ((*builtin)->flags & BUILTIN_HELP_LIST))
|
|
continue;
|
|
|
|
len = grub_strlen ((*builtin)->short_doc);
|
|
/* If the length of SHORT_DOC is too long, truncate it. */
|
|
if (len > MAX_SHORT_DOC_LEN - 1)
|
|
len = MAX_SHORT_DOC_LEN - 1;
|
|
|
|
for (i = 0; i < len; i++)
|
|
grub_putchar ((*builtin)->short_doc[i]);
|
|
|
|
for (; i < MAX_SHORT_DOC_LEN; i++)
|
|
grub_putchar (' ');
|
|
|
|
if (! left)
|
|
grub_putchar ('\n');
|
|
|
|
left = ! left;
|
|
}
|
|
|
|
/* 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
|
|
{
|
|
struct builtin **builtin;
|
|
char *next_arg;
|
|
|
|
/* Get the next argument. */
|
|
next_arg = skip_to (0, arg);
|
|
|
|
/* Terminate ARG. */
|
|
nul_terminate (arg);
|
|
|
|
for (builtin = builtin_table; *builtin; builtin++)
|
|
{
|
|
/* Skip this if this is only for the configuration file. */
|
|
if (! ((*builtin)->flags & BUILTIN_CMDLINE))
|
|
continue;
|
|
|
|
if (substring (arg, (*builtin)->name) < 1)
|
|
{
|
|
char *doc = (*builtin)->long_doc;
|
|
|
|
/* At first, print the name and the short doc. */
|
|
grub_printf ("%s: %s\n",
|
|
(*builtin)->name, (*builtin)->short_doc);
|
|
|
|
/* Print the long doc. */
|
|
while (*doc)
|
|
{
|
|
int len = grub_strlen (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. */
|
|
for (len = MAX_LONG_DOC_LEN; len > 0; len--)
|
|
if (doc[len - 1] == ' ')
|
|
break;
|
|
}
|
|
|
|
grub_printf (" ");
|
|
for (i = 0; i < len; i++)
|
|
grub_putchar (*doc++);
|
|
grub_putchar ('\n');
|
|
}
|
|
}
|
|
}
|
|
|
|
arg = next_arg;
|
|
}
|
|
while (*arg);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_help =
|
|
{
|
|
"help",
|
|
help_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"help [--all] [PATTERN ...]",
|
|
"Display helpful information about builtin commands. Not all commands"
|
|
" aren't shown without the option `--all'."
|
|
};
|
|
|
|
|
|
/* hiddenmenu */
|
|
static int
|
|
hiddenmenu_func (char *arg, int flags)
|
|
{
|
|
show_menu = 0;
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_hiddenmenu =
|
|
{
|
|
"hiddenmenu",
|
|
hiddenmenu_func,
|
|
BUILTIN_MENU,
|
|
#if 0
|
|
"hiddenmenu",
|
|
"Hide the menu."
|
|
#endif
|
|
};
|
|
|
|
|
|
/* hide */
|
|
static int
|
|
hide_func (char *arg, int flags)
|
|
{
|
|
if (! set_device (arg))
|
|
return 1;
|
|
|
|
if (! set_partition_hidden_flag (1))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_hide =
|
|
{
|
|
"hide",
|
|
hide_func,
|
|
BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
|
|
"hide PARTITION",
|
|
"Hide PARTITION by setting the \"hidden\" bit in"
|
|
" its partition type code."
|
|
};
|
|
|
|
|
|
#ifdef SUPPORT_NETBOOT
|
|
/* ifconfig */
|
|
static int
|
|
ifconfig_func (char *arg, int flags)
|
|
{
|
|
char *svr = 0, *ip = 0, *gw = 0, *sm = 0;
|
|
|
|
if (! eth_probe ())
|
|
{
|
|
grub_printf ("No ethernet card found.\n");
|
|
errnum = ERR_DEV_VALUES;
|
|
return 1;
|
|
}
|
|
|
|
while (*arg)
|
|
{
|
|
if (! grub_memcmp ("--server=", arg, sizeof ("--server=") - 1))
|
|
svr = arg + sizeof("--server=") - 1;
|
|
else if (! grub_memcmp ("--address=", arg, sizeof ("--address=") - 1))
|
|
ip = arg + sizeof ("--address=") - 1;
|
|
else if (! grub_memcmp ("--gateway=", arg, sizeof ("--gateway=") - 1))
|
|
gw = arg + sizeof ("--gateway=") - 1;
|
|
else if (! grub_memcmp ("--mask=", arg, sizeof("--mask=") - 1))
|
|
sm = arg + sizeof ("--mask=") - 1;
|
|
else
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
|
|
arg = skip_to (0, arg);
|
|
}
|
|
|
|
if (! ifconfig (ip, sm, gw, svr))
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
|
|
print_network_configuration ();
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_ifconfig =
|
|
{
|
|
"ifconfig",
|
|
ifconfig_func,
|
|
BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
|
|
"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
|
|
impsprobe_func (char *arg, int flags)
|
|
{
|
|
#ifdef GRUB_UTIL
|
|
/* In the grub shell, we cannot probe IMPS. */
|
|
errnum = ERR_UNRECOGNIZED;
|
|
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_func,
|
|
BUILTIN_CMDLINE,
|
|
"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
|
|
initrd_func (char *arg, int flags)
|
|
{
|
|
switch (kernel_type)
|
|
{
|
|
case KERNEL_TYPE_LINUX:
|
|
case KERNEL_TYPE_BIG_LINUX:
|
|
if (! load_initrd (arg))
|
|
return 1;
|
|
break;
|
|
|
|
default:
|
|
errnum = ERR_NEED_LX_KERNEL;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_initrd =
|
|
{
|
|
"initrd",
|
|
initrd_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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
|
|
install_func (char *arg, int flags)
|
|
{
|
|
char *stage1_file, *dest_dev, *file, *addr;
|
|
char *stage1_buffer = (char *) RAW_ADDR (0x100000);
|
|
char *stage2_buffer = stage1_buffer + SECTOR_SIZE;
|
|
char *old_sect = stage2_buffer + SECTOR_SIZE;
|
|
char *stage2_first_buffer = old_sect + SECTOR_SIZE;
|
|
char *stage2_second_buffer = stage2_first_buffer + SECTOR_SIZE;
|
|
/* XXX: Probably SECTOR_SIZE is reasonable. */
|
|
char *config_filename = stage2_second_buffer + SECTOR_SIZE;
|
|
char *dummy = config_filename + SECTOR_SIZE;
|
|
int new_drive = GRUB_INVALID_DRIVE;
|
|
int dest_drive, dest_partition, dest_sector;
|
|
int src_drive, src_partition, src_part_start;
|
|
int i;
|
|
struct geometry dest_geom, src_geom;
|
|
int saved_sector;
|
|
int stage2_first_sector, stage2_second_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. */
|
|
static void disk_read_savesect_func (int sector, int offset, int length)
|
|
{
|
|
if (debug)
|
|
printf ("[%d]", sector);
|
|
|
|
/* ReiserFS has files which sometimes contain data not aligned
|
|
on sector boundaries. Returning an error is better than
|
|
silently failing. */
|
|
if (offset != 0 || length != SECTOR_SIZE)
|
|
errnum = ERR_UNALIGNED;
|
|
|
|
saved_sector = sector;
|
|
}
|
|
|
|
/* Write SECTOR to INSTALLLIST, and update INSTALLADDR and
|
|
INSTALLSECT. */
|
|
static void disk_read_blocklist_func (int sector, int offset, int length)
|
|
{
|
|
if (debug)
|
|
printf("[%d]", sector);
|
|
|
|
if (offset != 0 || last_length != SECTOR_SIZE)
|
|
{
|
|
/* We found a non-sector-aligned data block. */
|
|
errnum = ERR_UNALIGNED;
|
|
return;
|
|
}
|
|
|
|
last_length = length;
|
|
|
|
if (*((unsigned long *) (installlist - 4))
|
|
+ *((unsigned short *) installlist) != sector
|
|
|| installlist == (int) stage2_first_buffer + SECTOR_SIZE + 4)
|
|
{
|
|
installlist -= 8;
|
|
|
|
if (*((unsigned long *) (installlist - 8)))
|
|
errnum = ERR_WONT_FIT;
|
|
else
|
|
{
|
|
*((unsigned short *) (installlist + 2)) = (installaddr >> 4);
|
|
*((unsigned long *) (installlist - 4)) = sector;
|
|
}
|
|
}
|
|
|
|
*((unsigned short *) installlist) += 1;
|
|
installaddr += 512;
|
|
}
|
|
|
|
/* First, check the GNU-style long option. */
|
|
while (1)
|
|
{
|
|
if (grub_memcmp ("--force-lba", arg, sizeof ("--force-lba") - 1) == 0)
|
|
{
|
|
is_force_lba = 1;
|
|
arg = skip_to (0, arg);
|
|
}
|
|
#ifdef GRUB_UTIL
|
|
else if (grub_memcmp ("--stage2=", arg, sizeof ("--stage2=") - 1) == 0)
|
|
{
|
|
stage2_os_file = arg + sizeof ("--stage2=") - 1;
|
|
arg = skip_to (0, arg);
|
|
nul_terminate (stage2_os_file);
|
|
}
|
|
#endif /* GRUB_UTIL */
|
|
else
|
|
break;
|
|
}
|
|
|
|
stage1_file = arg;
|
|
dest_dev = skip_to (0, stage1_file);
|
|
if (*dest_dev == 'd')
|
|
{
|
|
new_drive = 0;
|
|
dest_dev = skip_to (0, dest_dev);
|
|
}
|
|
file = skip_to (0, dest_dev);
|
|
addr = skip_to (0, file);
|
|
|
|
/* Get the installation address. */
|
|
if (! safe_parse_maxint (&addr, &installaddr))
|
|
{
|
|
/* ADDR is not specified. */
|
|
installaddr = 0;
|
|
ptr = addr;
|
|
errnum = 0;
|
|
}
|
|
else
|
|
ptr = skip_to (0, addr);
|
|
|
|
#ifndef NO_DECOMPRESSION
|
|
/* Do not decompress Stage 1 or Stage 2. */
|
|
no_decompression = 1;
|
|
#endif
|
|
|
|
/* Read Stage 1. */
|
|
is_open = grub_open (stage1_file);
|
|
if (! is_open
|
|
|| ! grub_read (stage1_buffer, SECTOR_SIZE) == SECTOR_SIZE)
|
|
goto fail;
|
|
|
|
/* Read the old sector from DEST_DEV. */
|
|
if (! set_device (dest_dev)
|
|
|| ! open_partition ()
|
|
|| ! devread (0, 0, SECTOR_SIZE, old_sect))
|
|
goto fail;
|
|
|
|
/* Store the information for the destination device. */
|
|
dest_drive = current_drive;
|
|
dest_partition = current_partition;
|
|
dest_geom = buf_geom;
|
|
dest_sector = part_start;
|
|
|
|
/* Copy the possible DOS BPB, 59 bytes at byte offset 3. */
|
|
grub_memmove (stage1_buffer + BOOTSEC_BPB_OFFSET,
|
|
old_sect + BOOTSEC_BPB_OFFSET,
|
|
BOOTSEC_BPB_LENGTH);
|
|
|
|
/* If for a hard disk, copy the possible MBR/extended part table. */
|
|
if (dest_drive & 0x80)
|
|
grub_memmove (stage1_buffer + STAGE1_WINDOWS_NT_MAGIC,
|
|
old_sect + STAGE1_WINDOWS_NT_MAGIC,
|
|
STAGE1_PARTEND - STAGE1_WINDOWS_NT_MAGIC);
|
|
|
|
/* Check for the version and the signature of Stage 1. */
|
|
if (*((short *)(stage1_buffer + STAGE1_VER_MAJ_OFFS)) != COMPAT_VERSION
|
|
|| (*((unsigned short *) (stage1_buffer + BOOTSEC_SIG_OFFSET))
|
|
!= BOOTSEC_SIGNATURE))
|
|
{
|
|
errnum = ERR_BAD_VERSION;
|
|
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)
|
|
&& (*((unsigned char *) (stage1_buffer + BOOTSEC_PART_OFFSET)) == 0x80
|
|
|| stage1_buffer[BOOTSEC_PART_OFFSET] == 0))
|
|
{
|
|
errnum = ERR_BAD_VERSION;
|
|
goto fail;
|
|
}
|
|
|
|
grub_close ();
|
|
|
|
/* Open Stage 2. */
|
|
is_open = grub_open (file);
|
|
if (! is_open)
|
|
goto fail;
|
|
|
|
src_drive = current_drive;
|
|
src_partition = current_partition;
|
|
src_part_start = part_start;
|
|
src_geom = buf_geom;
|
|
|
|
if (! new_drive)
|
|
new_drive = src_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. */
|
|
*((unsigned char *) (stage1_buffer + STAGE1_BOOT_DRIVE)) = new_drive;
|
|
|
|
/* Set the "force LBA" flag. */
|
|
*((unsigned char *) (stage1_buffer + STAGE1_FORCE_LBA)) = is_force_lba;
|
|
|
|
/* 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. */
|
|
disk_read_hook = disk_read_savesect_func;
|
|
if (grub_read (stage2_first_buffer, SECTOR_SIZE) != SECTOR_SIZE)
|
|
goto fail;
|
|
|
|
stage2_first_sector = saved_sector;
|
|
|
|
/* Read the second sector of Stage 2. */
|
|
if (grub_read (stage2_second_buffer, SECTOR_SIZE) != SECTOR_SIZE)
|
|
goto fail;
|
|
|
|
stage2_second_sector = saved_sector;
|
|
|
|
/* Check for the version of Stage 2. */
|
|
if (*((short *) (stage2_second_buffer + STAGE2_VER_MAJ_OFFS))
|
|
!= COMPAT_VERSION)
|
|
{
|
|
errnum = ERR_BAD_VERSION;
|
|
goto fail;
|
|
}
|
|
|
|
/* Check for the Stage 2 id. */
|
|
if (stage2_second_buffer[STAGE2_STAGE2_ID] != STAGE2_ID_STAGE2)
|
|
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))
|
|
= stage2_first_sector;
|
|
*((unsigned short *) (stage1_buffer + STAGE1_STAGE2_ADDRESS))
|
|
= installaddr;
|
|
*((unsigned short *) (stage1_buffer + STAGE1_STAGE2_SEGMENT))
|
|
= installaddr >> 4;
|
|
|
|
i = (int) stage2_first_buffer + SECTOR_SIZE - 4;
|
|
while (*((unsigned long *) i))
|
|
{
|
|
if (i < (int) stage2_first_buffer
|
|
|| (*((int *) (i - 4)) & 0x80000000)
|
|
|| *((unsigned short *) i) >= 0xA00
|
|
|| *((short *) (i + 2)) == 0)
|
|
{
|
|
errnum = ERR_BAD_VERSION;
|
|
goto fail;
|
|
}
|
|
|
|
*((int *) i) = 0;
|
|
*((int *) (i - 4)) = 0;
|
|
i -= 8;
|
|
}
|
|
|
|
installlist = (int) stage2_first_buffer + SECTOR_SIZE + 4;
|
|
installaddr += SECTOR_SIZE;
|
|
|
|
/* Read the whole of Stage2 except for the first sector. */
|
|
grub_seek (SECTOR_SIZE);
|
|
|
|
disk_read_hook = disk_read_blocklist_func;
|
|
if (! grub_read (dummy, -1))
|
|
goto fail;
|
|
|
|
disk_read_hook = 0;
|
|
|
|
/* Find a string for the configuration filename. */
|
|
config_file_location = stage2_second_buffer + STAGE2_VER_STR_OFFS;
|
|
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))
|
|
= src_partition;
|
|
if (is_stage1_5)
|
|
{
|
|
/* Reset the device information in FILE if it is a Stage 1.5. */
|
|
unsigned long device = 0xFFFFFFFF;
|
|
|
|
grub_memmove (config_file_location, (char *) &device,
|
|
sizeof (device));
|
|
}
|
|
|
|
ptr = skip_to (0, ptr);
|
|
}
|
|
|
|
if (*ptr)
|
|
{
|
|
grub_strcpy (config_filename, ptr);
|
|
nul_terminate (config_filename);
|
|
|
|
if (! is_stage1_5)
|
|
/* If it is a Stage 2, just copy PTR to CONFIG_FILE_LOCATION. */
|
|
grub_strcpy (config_file_location, ptr);
|
|
else
|
|
{
|
|
char *real_config;
|
|
unsigned long device;
|
|
|
|
/* Translate the external device syntax to the internal device
|
|
syntax. */
|
|
if (! (real_config = set_device (ptr)))
|
|
{
|
|
/* The Stage 2 PTR does not contain the device name, so
|
|
use the root device instead. */
|
|
errnum = ERR_NONE;
|
|
current_drive = saved_drive;
|
|
current_partition = saved_partition;
|
|
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. */
|
|
current_drive = GRUB_INVALID_DRIVE;
|
|
}
|
|
|
|
device = (current_drive << 24) | current_partition;
|
|
grub_memmove (config_file_location, (char *) &device,
|
|
sizeof (device));
|
|
grub_strcpy (config_file_location + sizeof (device),
|
|
real_config);
|
|
}
|
|
|
|
/* If a Stage 1.5 is used, then we need to modify the Stage2. */
|
|
if (is_stage1_5)
|
|
{
|
|
char *real_config_filename = skip_to (0, ptr);
|
|
|
|
is_open = grub_open (config_filename);
|
|
if (! is_open)
|
|
goto fail;
|
|
|
|
/* Skip the first sector. */
|
|
grub_seek (SECTOR_SIZE);
|
|
|
|
disk_read_hook = disk_read_savesect_func;
|
|
if (grub_read (stage2_buffer, SECTOR_SIZE) != SECTOR_SIZE)
|
|
goto fail;
|
|
|
|
disk_read_hook = 0;
|
|
grub_close ();
|
|
is_open = 0;
|
|
|
|
/* Sanity check. */
|
|
if (*(stage2_buffer + STAGE2_STAGE2_ID) != STAGE2_ID_STAGE2)
|
|
{
|
|
errnum = ERR_BAD_VERSION;
|
|
goto fail;
|
|
}
|
|
|
|
/* Set the "force LBA" flag for Stage2. */
|
|
*(stage2_buffer + STAGE2_FORCE_LBA) = is_force_lba;
|
|
|
|
/* 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. */
|
|
location = stage2_buffer + STAGE2_VER_STR_OFFS;
|
|
while (*(location++))
|
|
;
|
|
|
|
/* Copy the name. */
|
|
grub_strcpy (location, real_config_filename);
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
FILE *fp;
|
|
|
|
fp = fopen (stage2_os_file, "r+");
|
|
if (! fp)
|
|
{
|
|
errnum = ERR_FILE_NOT_FOUND;
|
|
goto fail;
|
|
}
|
|
|
|
if (fseek (fp, SECTOR_SIZE, SEEK_SET) != 0)
|
|
{
|
|
fclose (fp);
|
|
errnum = ERR_BAD_VERSION;
|
|
goto fail;
|
|
}
|
|
|
|
if (fwrite (stage2_buffer, 1, SECTOR_SIZE, fp)
|
|
!= SECTOR_SIZE)
|
|
{
|
|
fclose (fp);
|
|
errnum = ERR_WRITE;
|
|
goto fail;
|
|
}
|
|
|
|
fclose (fp);
|
|
}
|
|
else
|
|
#endif /* GRUB_UTIL */
|
|
{
|
|
if (! devwrite (saved_sector - part_start, 1, stage2_buffer))
|
|
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)
|
|
{
|
|
FILE *fp;
|
|
|
|
fp = fopen (stage2_os_file, "r+");
|
|
if (! fp)
|
|
{
|
|
errnum = ERR_FILE_NOT_FOUND;
|
|
goto fail;
|
|
}
|
|
|
|
if (fwrite (stage2_first_buffer, 1, SECTOR_SIZE, fp) != SECTOR_SIZE)
|
|
{
|
|
fclose (fp);
|
|
errnum = ERR_WRITE;
|
|
goto fail;
|
|
}
|
|
|
|
if (fwrite (stage2_second_buffer, 1, SECTOR_SIZE, fp) != SECTOR_SIZE)
|
|
{
|
|
fclose (fp);
|
|
errnum = ERR_WRITE;
|
|
goto fail;
|
|
}
|
|
|
|
fclose (fp);
|
|
}
|
|
else
|
|
#endif /* GRUB_UTIL */
|
|
{
|
|
/* The first. */
|
|
current_drive = src_drive;
|
|
current_partition = src_partition;
|
|
|
|
if (! open_partition ())
|
|
goto fail;
|
|
|
|
if (! devwrite (stage2_first_sector - src_part_start, 1,
|
|
stage2_first_buffer))
|
|
goto fail;
|
|
|
|
if (! devwrite (stage2_second_sector - src_part_start, 1,
|
|
stage2_second_buffer))
|
|
goto fail;
|
|
}
|
|
|
|
/* Write the modified sector of Stage 1 to the disk. */
|
|
current_drive = dest_drive;
|
|
current_partition = dest_partition;
|
|
if (! open_partition ())
|
|
goto fail;
|
|
|
|
devwrite (0, 1, stage1_buffer);
|
|
|
|
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_func,
|
|
BUILTIN_CMDLINE,
|
|
"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
|
|
ioprobe_func (char *arg, int flags)
|
|
{
|
|
#ifdef GRUB_UTIL
|
|
|
|
errnum = ERR_UNRECOGNIZED;
|
|
return 1;
|
|
|
|
#else /* ! GRUB_UTIL */
|
|
|
|
unsigned short *port;
|
|
|
|
/* Get the drive number. */
|
|
set_device (arg);
|
|
if (errnum)
|
|
return 1;
|
|
|
|
/* Clean out IO_MAP. */
|
|
grub_memset ((char *) io_map, 0, IO_MAP_SIZE * sizeof (unsigned short));
|
|
|
|
/* Track the int13 handler. */
|
|
track_int13 (current_drive);
|
|
|
|
/* Print out the result. */
|
|
for (port = io_map; *port != 0; port++)
|
|
grub_printf (" 0x%x", (unsigned int) *port);
|
|
|
|
return 0;
|
|
|
|
#endif /* ! GRUB_UTIL */
|
|
}
|
|
|
|
static struct builtin builtin_ioprobe =
|
|
{
|
|
"ioprobe",
|
|
ioprobe_func,
|
|
BUILTIN_CMDLINE,
|
|
"ioprobe DRIVE",
|
|
"Probe I/O ports used for the drive DRIVE."
|
|
};
|
|
|
|
|
|
/* kernel */
|
|
static int
|
|
kernel_func (char *arg, int flags)
|
|
{
|
|
int len;
|
|
kernel_t suggested_type = KERNEL_TYPE_NONE;
|
|
unsigned long load_flags = 0;
|
|
|
|
#ifndef AUTO_LINUX_MEM_OPT
|
|
load_flags |= KERNEL_LOAD_NO_MEM_OPTION;
|
|
#endif
|
|
|
|
/* Deal with GNU-style long options. */
|
|
while (1)
|
|
{
|
|
/* If the option `--type=TYPE' is specified, convert the string to
|
|
a kernel type. */
|
|
if (grub_memcmp (arg, "--type=", 7) == 0)
|
|
{
|
|
arg += 7;
|
|
|
|
if (grub_memcmp (arg, "netbsd", 6) == 0)
|
|
suggested_type = KERNEL_TYPE_NETBSD;
|
|
else if (grub_memcmp (arg, "freebsd", 7) == 0)
|
|
suggested_type = KERNEL_TYPE_FREEBSD;
|
|
else if (grub_memcmp (arg, "openbsd", 7) == 0)
|
|
/* XXX: For now, OpenBSD is identical to NetBSD, from GRUB's
|
|
point of view. */
|
|
suggested_type = KERNEL_TYPE_NETBSD;
|
|
else if (grub_memcmp (arg, "linux", 5) == 0)
|
|
suggested_type = KERNEL_TYPE_LINUX;
|
|
else if (grub_memcmp (arg, "biglinux", 8) == 0)
|
|
suggested_type = KERNEL_TYPE_BIG_LINUX;
|
|
else if (grub_memcmp (arg, "multiboot", 9) == 0)
|
|
suggested_type = KERNEL_TYPE_MULTIBOOT;
|
|
else
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
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 if (grub_memcmp (arg, "--no-mem-option", 15) == 0)
|
|
load_flags |= KERNEL_LOAD_NO_MEM_OPTION;
|
|
else
|
|
break;
|
|
|
|
/* Try the next. */
|
|
arg = skip_to (0, arg);
|
|
}
|
|
|
|
len = grub_strlen (arg);
|
|
|
|
/* Reset MB_CMDLINE. */
|
|
mb_cmdline = (char *) MB_CMDLINE_BUF;
|
|
if (len + 1 > MB_CMDLINE_BUFLEN)
|
|
{
|
|
errnum = ERR_WONT_FIT;
|
|
return 1;
|
|
}
|
|
|
|
/* Copy the command-line to MB_CMDLINE. */
|
|
grub_memmove (mb_cmdline, arg, len + 1);
|
|
kernel_type = load_image (arg, mb_cmdline, suggested_type, load_flags);
|
|
if (kernel_type == KERNEL_TYPE_NONE)
|
|
return 1;
|
|
|
|
mb_cmdline += len + 1;
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_kernel =
|
|
{
|
|
"kernel",
|
|
kernel_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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
|
|
lock_func (char *arg, int flags)
|
|
{
|
|
if (! auth && password)
|
|
{
|
|
errnum = ERR_PRIVILEGED;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_lock =
|
|
{
|
|
"lock",
|
|
lock_func,
|
|
BUILTIN_CMDLINE,
|
|
"lock",
|
|
"Break a command execution unless the user is authenticated."
|
|
};
|
|
|
|
|
|
/* makeactive */
|
|
static int
|
|
makeactive_func (char *arg, int flags)
|
|
{
|
|
if (! make_saved_active ())
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_makeactive =
|
|
{
|
|
"makeactive",
|
|
makeactive_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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
|
|
map_func (char *arg, int flags)
|
|
{
|
|
char *to_drive;
|
|
char *from_drive;
|
|
unsigned long to, from;
|
|
int i;
|
|
|
|
to_drive = arg;
|
|
from_drive = skip_to (0, arg);
|
|
|
|
/* Get the drive number for TO_DRIVE. */
|
|
set_device (to_drive);
|
|
if (errnum)
|
|
return 1;
|
|
to = current_drive;
|
|
|
|
/* Get the drive number for FROM_DRIVE. */
|
|
set_device (from_drive);
|
|
if (errnum)
|
|
return 1;
|
|
from = current_drive;
|
|
|
|
/* 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. */
|
|
if ((bios_drive_map[i] & 0xff) == from)
|
|
break;
|
|
|
|
if (! bios_drive_map[i])
|
|
break;
|
|
}
|
|
|
|
if (i == DRIVE_MAP_SIZE)
|
|
{
|
|
errnum = ERR_WONT_FIT;
|
|
return 1;
|
|
}
|
|
|
|
if (to == from)
|
|
/* If TO is equal to FROM, delete the entry. */
|
|
grub_memmove ((char *) &bios_drive_map[i], (char *) &bios_drive_map[i + 1],
|
|
sizeof (unsigned short) * (DRIVE_MAP_SIZE - i));
|
|
else
|
|
bios_drive_map[i] = from | (to << 8);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_map =
|
|
{
|
|
"map",
|
|
map_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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
|
|
md5crypt_func (char *arg, int flags)
|
|
{
|
|
char crypted[36];
|
|
char key[32];
|
|
unsigned int seed;
|
|
int i;
|
|
const char *const seedchars =
|
|
"./0123456789ABCDEFGHIJKLMNOPQRST"
|
|
"UVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
|
|
/* First create a salt. */
|
|
|
|
/* The magical prefix. */
|
|
grub_memset (crypted, 0, sizeof (crypted));
|
|
grub_memmove (crypted, "$1$", 3);
|
|
|
|
/* Create the length of a salt. */
|
|
seed = currticks ();
|
|
|
|
/* Generate a salt. */
|
|
for (i = 0; i < 8 && seed; i++)
|
|
{
|
|
/* FIXME: This should be more random. */
|
|
crypted[3 + i] = seedchars[seed & 0x3f];
|
|
seed >>= 6;
|
|
}
|
|
|
|
/* A salt must be terminated with `$', if it is less than 8 chars. */
|
|
crypted[3 + i] = '$';
|
|
|
|
#ifdef DEBUG_MD5CRYPT
|
|
grub_printf ("salt = %s\n", crypted);
|
|
#endif
|
|
|
|
/* Get a password. */
|
|
grub_memset (key, 0, sizeof (key));
|
|
get_cmdline ("Password: ", key, sizeof (key) - 1, '*', 0);
|
|
|
|
/* Crypt the key. */
|
|
make_md5_password (key, crypted);
|
|
|
|
grub_printf ("Encrypted: %s\n", crypted);
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_md5crypt =
|
|
{
|
|
"md5crypt",
|
|
md5crypt_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"md5crypt",
|
|
"Generate a password in MD5 format."
|
|
};
|
|
#endif /* USE_MD5_PASSWORDS */
|
|
|
|
|
|
/* module */
|
|
static int
|
|
module_func (char *arg, int flags)
|
|
{
|
|
int len = grub_strlen (arg);
|
|
|
|
switch (kernel_type)
|
|
{
|
|
case KERNEL_TYPE_MULTIBOOT:
|
|
if (mb_cmdline + len + 1 > (char *) MB_CMDLINE_BUF + MB_CMDLINE_BUFLEN)
|
|
{
|
|
errnum = ERR_WONT_FIT;
|
|
return 1;
|
|
}
|
|
grub_memmove (mb_cmdline, arg, len + 1);
|
|
if (! load_module (arg, mb_cmdline))
|
|
return 1;
|
|
mb_cmdline += len + 1;
|
|
break;
|
|
|
|
case KERNEL_TYPE_LINUX:
|
|
case KERNEL_TYPE_BIG_LINUX:
|
|
if (! load_initrd (arg))
|
|
return 1;
|
|
break;
|
|
|
|
default:
|
|
errnum = ERR_NEED_MB_KERNEL;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_module =
|
|
{
|
|
"module",
|
|
module_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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
|
|
modulenounzip_func (char *arg, int flags)
|
|
{
|
|
int ret;
|
|
|
|
#ifndef NO_DECOMPRESSION
|
|
no_decompression = 1;
|
|
#endif
|
|
|
|
ret = module_func (arg, flags);
|
|
|
|
#ifndef NO_DECOMPRESSION
|
|
no_decompression = 0;
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct builtin builtin_modulenounzip =
|
|
{
|
|
"modulenounzip",
|
|
modulenounzip_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"modulenounzip FILE [ARG ...]",
|
|
"The same as `module', except that automatic decompression is"
|
|
" disabled."
|
|
};
|
|
|
|
|
|
/* pager [on|off] */
|
|
static int
|
|
pager_func (char *arg, int flags)
|
|
{
|
|
/* If ARG is empty, toggle the flag. */
|
|
if (! *arg)
|
|
use_pager = ! use_pager;
|
|
else if (grub_memcmp (arg, "on", 2) == 0)
|
|
use_pager = 1;
|
|
else if (grub_memcmp (arg, "off", 3) == 0)
|
|
use_pager = 0;
|
|
else
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
|
|
grub_printf (" Internal pager is now %s\n", use_pager ? "on" : "off");
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_pager =
|
|
{
|
|
"pager",
|
|
pager_func,
|
|
BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
|
|
"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
|
|
partnew_func (char *arg, int flags)
|
|
{
|
|
int new_type, new_start, new_len;
|
|
int start_cl, start_ch, start_dh;
|
|
int end_cl, end_ch, end_dh;
|
|
int entry;
|
|
char mbr[512];
|
|
|
|
/* Convert a LBA address to a CHS address in the INT 13 format. */
|
|
auto void lba_to_chs (int lba, int *cl, int *ch, int *dh);
|
|
void lba_to_chs (int lba, int *cl, int *ch, int *dh)
|
|
{
|
|
int cylinder, head, sector;
|
|
|
|
sector = lba % buf_geom.sectors + 1;
|
|
head = (lba / buf_geom.sectors) % buf_geom.heads;
|
|
cylinder = lba / (buf_geom.sectors * buf_geom.heads);
|
|
|
|
if (cylinder >= buf_geom.cylinders)
|
|
cylinder = buf_geom.cylinders - 1;
|
|
|
|
*cl = sector | ((cylinder & 0x300) >> 2);
|
|
*ch = cylinder & 0xFF;
|
|
*dh = head;
|
|
}
|
|
|
|
/* Get the drive and the partition. */
|
|
if (! set_device (arg))
|
|
return 1;
|
|
|
|
/* The drive must be a hard disk. */
|
|
if (! (current_drive & 0x80))
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
|
|
/* The partition must a primary partition. */
|
|
if ((current_partition >> 16) > 3
|
|
|| (current_partition & 0xFFFF) != 0xFFFF)
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
|
|
entry = current_partition >> 16;
|
|
|
|
/* Get the new partition type. */
|
|
arg = skip_to (0, arg);
|
|
if (! safe_parse_maxint (&arg, &new_type))
|
|
return 1;
|
|
|
|
/* The partition type is unsigned char. */
|
|
if (new_type > 0xFF)
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
|
|
/* Get the new partition start. */
|
|
arg = skip_to (0, arg);
|
|
if (! safe_parse_maxint (&arg, &new_start))
|
|
return 1;
|
|
|
|
/* Get the new partition length. */
|
|
arg = skip_to (0, arg);
|
|
if (! safe_parse_maxint (&arg, &new_len))
|
|
return 1;
|
|
|
|
/* Read the MBR. */
|
|
if (! rawread (current_drive, 0, 0, SECTOR_SIZE, mbr))
|
|
return 1;
|
|
|
|
/* Check if the new partition will fit in the disk. */
|
|
if (new_start + new_len > buf_geom.total_sectors)
|
|
{
|
|
errnum = ERR_GEOM;
|
|
return 1;
|
|
}
|
|
|
|
/* Store the partition information in the MBR. */
|
|
lba_to_chs (new_start, &start_cl, &start_ch, &start_dh);
|
|
lba_to_chs (new_start + new_len - 1, &end_cl, &end_ch, &end_dh);
|
|
|
|
PC_SLICE_FLAG (mbr, entry) = 0;
|
|
PC_SLICE_HEAD (mbr, entry) = start_dh;
|
|
PC_SLICE_SEC (mbr, entry) = start_cl;
|
|
PC_SLICE_CYL (mbr, entry) = start_ch;
|
|
PC_SLICE_TYPE (mbr, entry) = new_type;
|
|
PC_SLICE_EHEAD (mbr, entry) = end_dh;
|
|
PC_SLICE_ESEC (mbr, entry) = end_cl;
|
|
PC_SLICE_ECYL (mbr, entry) = end_ch;
|
|
PC_SLICE_START (mbr, entry) = new_start;
|
|
PC_SLICE_LENGTH (mbr, entry) = new_len;
|
|
|
|
/* Make sure that the MBR has a valid signature. */
|
|
PC_MBR_SIG (mbr) = PC_MBR_SIGNATURE;
|
|
|
|
/* Write back the MBR to the disk. */
|
|
buf_track = -1;
|
|
if (! rawwrite (current_drive, 0, mbr))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_partnew =
|
|
{
|
|
"partnew",
|
|
partnew_func,
|
|
BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
|
|
"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
|
|
parttype_func (char *arg, int flags)
|
|
{
|
|
int new_type;
|
|
unsigned long part = 0xFFFFFF;
|
|
unsigned long start, len, offset, ext_offset;
|
|
int entry, type;
|
|
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))
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
|
|
/* The partition must be a PC slice. */
|
|
if ((current_partition >> 16) == 0xFF
|
|
|| (current_partition & 0xFFFF) != 0xFFFF)
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
|
|
/* Get the new partition type. */
|
|
arg = skip_to (0, arg);
|
|
if (! safe_parse_maxint (&arg, &new_type))
|
|
return 1;
|
|
|
|
/* The partition type is unsigned char. */
|
|
if (new_type > 0xFF)
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
|
|
/* Look for the partition. */
|
|
while (next_partition (current_drive, 0xFFFFFF, &part, &type,
|
|
&start, &len, &offset, &entry,
|
|
&ext_offset, mbr))
|
|
{
|
|
if (part == current_partition)
|
|
{
|
|
/* Found. */
|
|
|
|
/* Set the type to NEW_TYPE. */
|
|
PC_SLICE_TYPE (mbr, entry) = new_type;
|
|
|
|
/* Write back the MBR to the disk. */
|
|
buf_track = -1;
|
|
if (! rawwrite (current_drive, offset, mbr))
|
|
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_func,
|
|
BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
|
|
"parttype PART TYPE",
|
|
"Change the type of the partition PART to TYPE."
|
|
};
|
|
|
|
|
|
/* password */
|
|
static int
|
|
password_func (char *arg, int flags)
|
|
{
|
|
int len;
|
|
password_t type = PASSWORD_PLAIN;
|
|
|
|
#ifdef USE_MD5_PASSWORDS
|
|
if (grub_memcmp (arg, "--md5", 5) == 0)
|
|
{
|
|
type = PASSWORD_MD5;
|
|
arg = skip_to (0, arg);
|
|
}
|
|
#endif
|
|
if (grub_memcmp (arg, "--", 2) == 0)
|
|
{
|
|
type = PASSWORD_UNSUPPORTED;
|
|
arg = skip_to (0, arg);
|
|
}
|
|
|
|
if ((flags & (BUILTIN_CMDLINE | BUILTIN_SCRIPT)) != 0)
|
|
{
|
|
/* Do password check! */
|
|
char entered[32];
|
|
|
|
/* Wipe out any previously entered password */
|
|
entered[0] = 0;
|
|
get_cmdline ("Password: ", entered, 31, '*', 0);
|
|
|
|
nul_terminate (arg);
|
|
if (check_password (entered, arg, type) != 0)
|
|
{
|
|
errnum = ERR_PRIVILEGED;
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
len = grub_strlen (arg);
|
|
|
|
/* PASSWORD NUL NUL ... */
|
|
if (len + 2 > PASSWORD_BUFLEN)
|
|
{
|
|
errnum = ERR_WONT_FIT;
|
|
return 1;
|
|
}
|
|
|
|
/* Copy the password and clear the rest of the buffer. */
|
|
password = (char *) PASSWORD_BUF;
|
|
grub_memmove (password, arg, len);
|
|
grub_memset (password + len, 0, PASSWORD_BUFLEN - len);
|
|
password_type = type;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_password =
|
|
{
|
|
"password",
|
|
password_func,
|
|
BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_NO_ECHO,
|
|
"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
|
|
pause_func (char *arg, int flags)
|
|
{
|
|
printf("%s\n", arg);
|
|
|
|
/* If ESC is returned, then abort this entry. */
|
|
if (ASCII_CHAR (getkey ()) == 27)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_pause =
|
|
{
|
|
"pause",
|
|
pause_func,
|
|
BUILTIN_CMDLINE | BUILTIN_NO_ECHO,
|
|
"pause [MESSAGE ...]",
|
|
"Print MESSAGE, then wait until a key is pressed."
|
|
};
|
|
|
|
|
|
#ifdef GRUB_UTIL
|
|
/* quit */
|
|
static int
|
|
quit_func (char *arg, int flags)
|
|
{
|
|
stop ();
|
|
|
|
/* Never reach here. */
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_quit =
|
|
{
|
|
"quit",
|
|
quit_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"quit",
|
|
"Exit from the GRUB shell."
|
|
};
|
|
#endif /* GRUB_UTIL */
|
|
|
|
|
|
#ifdef SUPPORT_NETBOOT
|
|
/* rarp */
|
|
static int
|
|
rarp_func (char *arg, int flags)
|
|
{
|
|
if (! rarp ())
|
|
{
|
|
if (errnum == ERR_NONE)
|
|
errnum = ERR_DEV_VALUES;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Notify the configuration. */
|
|
print_network_configuration ();
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_rarp =
|
|
{
|
|
"rarp",
|
|
rarp_func,
|
|
BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
|
|
"rarp",
|
|
"Initialize a network device via RARP."
|
|
};
|
|
#endif /* SUPPORT_NETBOOT */
|
|
|
|
|
|
static int
|
|
read_func (char *arg, int flags)
|
|
{
|
|
int addr;
|
|
|
|
if (! safe_parse_maxint (&arg, &addr))
|
|
return 1;
|
|
|
|
grub_printf ("Address 0x%x: Value 0x%x\n",
|
|
addr, *((unsigned *) RAW_ADDR (addr)));
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_read =
|
|
{
|
|
"read",
|
|
read_func,
|
|
BUILTIN_CMDLINE,
|
|
"read ADDR",
|
|
"Read a 32-bit value from memory at address ADDR and"
|
|
" display it in hex format."
|
|
};
|
|
|
|
|
|
/* reboot */
|
|
static int
|
|
reboot_func (char *arg, int flags)
|
|
{
|
|
grub_reboot ();
|
|
|
|
/* Never reach here. */
|
|
return 1;
|
|
}
|
|
|
|
static struct builtin builtin_reboot =
|
|
{
|
|
"reboot",
|
|
reboot_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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 (" (hd%d", saved_drive - 0x80);
|
|
|
|
if ((saved_partition & 0xFF0000) != 0xFF0000)
|
|
grub_printf (",%d", saved_partition >> 16);
|
|
|
|
if ((saved_partition & 0x00FF00) != 0x00FF00)
|
|
grub_printf (",%c", ((saved_partition >> 8) & 0xFF) + 'a');
|
|
|
|
grub_printf ("):");
|
|
}
|
|
else
|
|
{
|
|
/* Floppy disk drive. */
|
|
grub_printf (" (fd%d):", saved_drive);
|
|
}
|
|
|
|
/* Print the filesystem information. */
|
|
current_partition = saved_partition;
|
|
current_drive = saved_drive;
|
|
print_fsys_type ();
|
|
}
|
|
|
|
static int
|
|
real_root_func (char *arg, int attempt_mount)
|
|
{
|
|
int hdbias = 0;
|
|
char *biasptr;
|
|
char *next;
|
|
|
|
/* If ARG is empty, just print the current root device. */
|
|
if (! *arg)
|
|
{
|
|
print_root_device ();
|
|
return 0;
|
|
}
|
|
|
|
/* Call set_device to get the drive and the partition in ARG. */
|
|
next = set_device (arg);
|
|
if (! next)
|
|
return 1;
|
|
|
|
/* Ignore ERR_FSYS_MOUNT. */
|
|
if (attempt_mount)
|
|
{
|
|
if (! open_device () && errnum != ERR_FSYS_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;
|
|
saved_partition = current_partition;
|
|
saved_drive = current_drive;
|
|
|
|
if (attempt_mount)
|
|
{
|
|
/* BSD and chainloading evil hacks !! */
|
|
biasptr = skip_to (0, next);
|
|
safe_parse_maxint (&biasptr, &hdbias);
|
|
errnum = 0;
|
|
bootdev = set_bootdev (hdbias);
|
|
if (errnum)
|
|
return 1;
|
|
|
|
/* Print the type of the filesystem. */
|
|
print_fsys_type ();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
root_func (char *arg, int flags)
|
|
{
|
|
return real_root_func (arg, 1);
|
|
}
|
|
|
|
static struct builtin builtin_root =
|
|
{
|
|
"root",
|
|
root_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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
|
|
rootnoverify_func (char *arg, int flags)
|
|
{
|
|
return real_root_func (arg, 0);
|
|
}
|
|
|
|
static struct builtin builtin_rootnoverify =
|
|
{
|
|
"rootnoverify",
|
|
rootnoverify_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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
|
|
savedefault_func (char *arg, int flags)
|
|
{
|
|
#if !defined(SUPPORT_DISKLESS) && !defined(GRUB_UTIL)
|
|
char buffer[512];
|
|
int *entryno_ptr;
|
|
|
|
/* This command is only useful when you boot an entry from the menu
|
|
interface. */
|
|
if (! (flags & BUILTIN_SCRIPT))
|
|
{
|
|
errnum = ERR_UNRECOGNIZED;
|
|
return 1;
|
|
}
|
|
|
|
/* Get the geometry of the boot drive (i.e. the disk which contains
|
|
this stage2). */
|
|
if (get_diskinfo (boot_drive, &buf_geom))
|
|
{
|
|
errnum = ERR_NO_DISK;
|
|
return 1;
|
|
}
|
|
|
|
/* Load the second sector of this stage2. */
|
|
if (! rawread (boot_drive, install_second_sector, 0, SECTOR_SIZE, buffer))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
/* Sanity check. */
|
|
if (buffer[STAGE2_STAGE2_ID] != STAGE2_ID_STAGE2
|
|
|| *((short *) (buffer + STAGE2_VER_MAJ_OFFS)) != COMPAT_VERSION)
|
|
{
|
|
errnum = ERR_BAD_VERSION;
|
|
return 1;
|
|
}
|
|
|
|
entryno_ptr = (int *) (buffer + STAGE2_SAVED_ENTRYNO);
|
|
|
|
/* Check if the saved entry number differs from current entry number. */
|
|
if (*entryno_ptr != current_entryno)
|
|
{
|
|
/* Overwrite the saved entry number. */
|
|
*entryno_ptr = current_entryno;
|
|
|
|
/* Save the image in the disk. */
|
|
if (! rawwrite (boot_drive, install_second_sector, buffer))
|
|
return 1;
|
|
|
|
/* Clear the cache. */
|
|
buf_track = -1;
|
|
}
|
|
|
|
return 0;
|
|
#else /* ! SUPPORT_DISKLESS && ! GRUB_UTIL */
|
|
errnum = ERR_UNRECOGNIZED;
|
|
return 1;
|
|
#endif /* ! SUPPORT_DISKLESS && ! GRUB_UTIL */
|
|
}
|
|
|
|
static struct builtin builtin_savedefault =
|
|
{
|
|
"savedefault",
|
|
savedefault_func,
|
|
BUILTIN_CMDLINE,
|
|
"savedefault",
|
|
"Save the current entry as the default boot entry."
|
|
};
|
|
|
|
|
|
#ifdef SUPPORT_SERIAL
|
|
/* serial */
|
|
static int
|
|
serial_func (char *arg, int flags)
|
|
{
|
|
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)
|
|
{
|
|
if (grub_memcmp (arg, "--unit=", sizeof ("--unit=") - 1) == 0)
|
|
{
|
|
char *p = arg + sizeof ("--unit=") - 1;
|
|
int unit;
|
|
|
|
if (! safe_parse_maxint (&p, &unit))
|
|
return 1;
|
|
|
|
if (unit < 0 || unit > 3)
|
|
{
|
|
errnum = ERR_DEV_VALUES;
|
|
return 1;
|
|
}
|
|
|
|
port = serial_hw_get_port (unit);
|
|
}
|
|
else if (grub_memcmp (arg, "--speed=", sizeof ("--speed=") - 1) == 0)
|
|
{
|
|
char *p = arg + sizeof ("--speed=") - 1;
|
|
int num;
|
|
|
|
if (! safe_parse_maxint (&p, &num))
|
|
return 1;
|
|
|
|
speed = (unsigned int) num;
|
|
}
|
|
else if (grub_memcmp (arg, "--port=", sizeof ("--port=") - 1) == 0)
|
|
{
|
|
char *p = arg + sizeof ("--port=") - 1;
|
|
int num;
|
|
|
|
if (! safe_parse_maxint (&p, &num))
|
|
return 1;
|
|
|
|
port = (unsigned short) num;
|
|
}
|
|
else if (grub_memcmp (arg, "--word=", sizeof ("--word=") - 1) == 0)
|
|
{
|
|
char *p = arg + sizeof ("--word=") - 1;
|
|
int len;
|
|
|
|
if (! safe_parse_maxint (&p, &len))
|
|
return 1;
|
|
|
|
switch (len)
|
|
{
|
|
case 5: word_len = UART_5BITS_WORD; break;
|
|
case 6: word_len = UART_6BITS_WORD; break;
|
|
case 7: word_len = UART_7BITS_WORD; break;
|
|
case 8: word_len = UART_8BITS_WORD; break;
|
|
default:
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
}
|
|
else if (grub_memcmp (arg, "--stop=", sizeof ("--stop=") - 1) == 0)
|
|
{
|
|
char *p = arg + sizeof ("--stop=") - 1;
|
|
int len;
|
|
|
|
if (! safe_parse_maxint (&p, &len))
|
|
return 1;
|
|
|
|
switch (len)
|
|
{
|
|
case 1: stop_bit_len = UART_1_STOP_BIT; break;
|
|
case 2: stop_bit_len = UART_2_STOP_BITS; break;
|
|
default:
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
}
|
|
else if (grub_memcmp (arg, "--parity=", sizeof ("--parity=") - 1) == 0)
|
|
{
|
|
char *p = arg + sizeof ("--parity=") - 1;
|
|
|
|
if (grub_memcmp (p, "no", sizeof ("no") - 1) == 0)
|
|
parity = UART_NO_PARITY;
|
|
else if (grub_memcmp (p, "odd", sizeof ("odd") - 1) == 0)
|
|
parity = UART_ODD_PARITY;
|
|
else if (grub_memcmp (p, "even", sizeof ("even") - 1) == 0)
|
|
parity = UART_EVEN_PARITY;
|
|
else
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
}
|
|
# ifdef GRUB_UTIL
|
|
/* In the grub shell, don't use any port number but open a tty
|
|
device instead. */
|
|
else if (grub_memcmp (arg, "--device=", sizeof ("--device=") - 1) == 0)
|
|
{
|
|
char *p = arg + sizeof ("--device=") - 1;
|
|
char dev[256]; /* XXX */
|
|
char *q = dev;
|
|
|
|
while (*p && ! grub_isspace (*p))
|
|
*q++ = *p++;
|
|
|
|
*q = 0;
|
|
serial_set_device (dev);
|
|
}
|
|
# endif /* GRUB_UTIL */
|
|
else
|
|
break;
|
|
|
|
arg = skip_to (0, arg);
|
|
}
|
|
|
|
/* Initialize the serial unit. */
|
|
if (! serial_hw_init (port, speed, word_len, parity, stop_bit_len))
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_serial =
|
|
{
|
|
"serial",
|
|
serial_func,
|
|
BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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
|
|
setkey_func (char *arg, int flags)
|
|
{
|
|
char *to_key, *from_key;
|
|
int to_code, from_code;
|
|
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 &&
|
|
grub_strcmp (key, keysym_table[i].unshifted_name) == 0)
|
|
return keysym_table[i].keycode;
|
|
else if (keysym_table[i].shifted_name &&
|
|
grub_strcmp (key, keysym_table[i].shifted_name) == 0)
|
|
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 &&
|
|
grub_strcmp (key, keysym_table[i].unshifted_name) == 0)
|
|
return keysym_table[i].unshifted_ascii;
|
|
else if (keysym_table[i].shifted_name &&
|
|
grub_strcmp (key, keysym_table[i].shifted_name) == 0)
|
|
return keysym_table[i].shifted_ascii;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
to_key = arg;
|
|
from_key = skip_to (0, to_key);
|
|
|
|
if (! *to_key)
|
|
{
|
|
/* If the user specifies no argument, reset the key mappings. */
|
|
grub_memset (bios_key_map, 0, KEY_MAP_SIZE * sizeof (unsigned short));
|
|
grub_memset (ascii_key_map, 0, KEY_MAP_SIZE * sizeof (unsigned short));
|
|
|
|
return 0;
|
|
}
|
|
else if (! *from_key)
|
|
{
|
|
/* The user must specify two arguments or zero argument. */
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
|
|
nul_terminate (to_key);
|
|
nul_terminate (from_key);
|
|
|
|
to_code = find_ascii_code (to_key);
|
|
from_code = find_ascii_code (from_key);
|
|
if (! to_code || ! from_code)
|
|
{
|
|
map_in_interrupt = 1;
|
|
to_code = find_key_code (to_key);
|
|
from_code = find_key_code (from_key);
|
|
if (! to_code || ! from_code)
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (map_in_interrupt)
|
|
{
|
|
int i;
|
|
|
|
/* Find an empty slot. */
|
|
for (i = 0; i < KEY_MAP_SIZE; i++)
|
|
{
|
|
if ((bios_key_map[i] & 0xff) == from_code)
|
|
/* Perhaps the user wants to overwrite the map. */
|
|
break;
|
|
|
|
if (! bios_key_map[i])
|
|
break;
|
|
}
|
|
|
|
if (i == KEY_MAP_SIZE)
|
|
{
|
|
errnum = ERR_WONT_FIT;
|
|
return 1;
|
|
}
|
|
|
|
if (to_code == from_code)
|
|
/* 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
|
|
bios_key_map[i] = (to_code << 8) | from_code;
|
|
|
|
/* Ugly but should work. */
|
|
unset_int15_handler ();
|
|
set_int15_handler ();
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
|
|
/* Find an empty slot. */
|
|
for (i = 0; i < KEY_MAP_SIZE; i++)
|
|
{
|
|
if ((ascii_key_map[i] & 0xff) == from_code)
|
|
/* Perhaps the user wants to overwrite the map. */
|
|
break;
|
|
|
|
if (! ascii_key_map[i])
|
|
break;
|
|
}
|
|
|
|
if (i == KEY_MAP_SIZE)
|
|
{
|
|
errnum = ERR_WONT_FIT;
|
|
return 1;
|
|
}
|
|
|
|
if (to_code == from_code)
|
|
/* 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
|
|
ascii_key_map[i] = (to_code << 8) | from_code;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_setkey =
|
|
{
|
|
"setkey",
|
|
setkey_func,
|
|
BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
|
|
"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
|
|
setup_func (char *arg, int flags)
|
|
{
|
|
/* Point to the string of the installed drive/partition. */
|
|
char *install_ptr;
|
|
/* Point to the string of the drive/parition where the GRUB images
|
|
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];
|
|
char *buffer = (char *) RAW_ADDR (0x100000);
|
|
int is_force_lba = 0;
|
|
char *stage2_arg = 0;
|
|
char *prefix = 0;
|
|
|
|
auto int check_file (char *file);
|
|
auto void sprint_device (int drive, int partition);
|
|
auto int embed_stage1_5 (char * stage1_5, int drive, int partition);
|
|
|
|
/* Check if the file FILE exists like Autoconf. */
|
|
int check_file (char *file)
|
|
{
|
|
int ret;
|
|
|
|
grub_printf (" Checking if \"%s\" exists... ", file);
|
|
ret = grub_open (file);
|
|
if (ret)
|
|
{
|
|
grub_close ();
|
|
grub_printf ("yes\n");
|
|
}
|
|
else
|
|
grub_printf ("no\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Construct a device name in DEVICE. */
|
|
void sprint_device (int drive, int partition)
|
|
{
|
|
grub_sprintf (device, "(%cd%d",
|
|
(drive & 0x80) ? 'h' : 'f',
|
|
drive & ~0x80);
|
|
if ((partition & 0xFF0000) != 0xFF0000)
|
|
{
|
|
char tmp[16];
|
|
grub_sprintf (tmp, ",%d", (partition >> 16) & 0xFF);
|
|
grub_strncat (device, tmp, 256);
|
|
}
|
|
if ((partition & 0x00FF00) != 0x00FF00)
|
|
{
|
|
char tmp[16];
|
|
grub_sprintf (tmp, ",%c", 'a' + ((partition >> 8) & 0xFF));
|
|
grub_strncat (device, tmp, 256);
|
|
}
|
|
grub_strncat (device, ")", 256);
|
|
}
|
|
|
|
int embed_stage1_5 (char *stage1_5, int drive, int partition)
|
|
{
|
|
/* We install GRUB into the MBR, so try to embed the
|
|
Stage 1.5 in the sectors right after the MBR. */
|
|
sprint_device (drive, partition);
|
|
grub_sprintf (cmd_arg, "%s %s", stage1_5, device);
|
|
|
|
/* Notify what will be run. */
|
|
grub_printf (" Running \"embed %s\"... ", cmd_arg);
|
|
|
|
embed_func (cmd_arg, flags);
|
|
if (! errnum)
|
|
{
|
|
/* Construct the blocklist representation. */
|
|
grub_sprintf (buffer, "%s%s", device, embed_info);
|
|
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"}
|
|
};
|
|
|
|
tmp_drive = saved_drive;
|
|
tmp_partition = saved_partition;
|
|
|
|
/* Check if the user specifies --force-lba. */
|
|
while (1)
|
|
{
|
|
if (grub_memcmp ("--force-lba", arg, sizeof ("--force-lba") - 1) == 0)
|
|
{
|
|
is_force_lba = 1;
|
|
arg = skip_to (0, arg);
|
|
}
|
|
else if (grub_memcmp ("--prefix=", arg, sizeof ("--prefix=") - 1) == 0)
|
|
{
|
|
prefix = arg + sizeof ("--prefix=") - 1;
|
|
arg = skip_to (0, arg);
|
|
nul_terminate (prefix);
|
|
}
|
|
#ifdef GRUB_UTIL
|
|
else if (grub_memcmp ("--stage2=", arg, sizeof ("--stage2=") - 1) == 0)
|
|
{
|
|
stage2_arg = arg;
|
|
arg = skip_to (0, arg);
|
|
nul_terminate (stage2_arg);
|
|
}
|
|
#endif /* GRUB_UTIL */
|
|
else
|
|
break;
|
|
}
|
|
|
|
install_ptr = arg;
|
|
image_ptr = skip_to (0, install_ptr);
|
|
|
|
/* Make sure that INSTALL_PTR is valid. */
|
|
set_device (install_ptr);
|
|
if (errnum)
|
|
return 1;
|
|
|
|
installed_drive = current_drive;
|
|
installed_partition = current_partition;
|
|
|
|
/* Mount the drive pointed by IMAGE_PTR. */
|
|
if (*image_ptr)
|
|
{
|
|
/* If the drive/partition where the images reside is specified,
|
|
get the drive and the partition. */
|
|
set_device (image_ptr);
|
|
if (errnum)
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
/* If omitted, use SAVED_PARTITION and SAVED_DRIVE. */
|
|
current_drive = saved_drive;
|
|
current_partition = saved_partition;
|
|
}
|
|
|
|
image_drive = saved_drive = current_drive;
|
|
image_partition = saved_partition = current_partition;
|
|
|
|
/* Open it. */
|
|
if (! open_device ())
|
|
goto fail;
|
|
|
|
/* Check if stage1 exists. If the user doesn't specify the option
|
|
`--prefix', attempt /boot/grub and /grub. */
|
|
/* NOTE: It is dangerous to run this command without `--prefix' in the
|
|
grub shell, since that affects `--stage2'. */
|
|
if (! prefix)
|
|
{
|
|
prefix = "/boot/grub";
|
|
grub_sprintf (stage1, "%s%s", prefix, "/stage1");
|
|
if (! check_file (stage1))
|
|
{
|
|
errnum = ERR_NONE;
|
|
prefix = "/grub";
|
|
grub_sprintf (stage1, "%s%s", prefix, "/stage1");
|
|
if (! check_file (stage1))
|
|
goto fail;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
grub_sprintf (stage1, "%s%s", prefix, "/stage1");
|
|
if (! check_file (stage1))
|
|
goto fail;
|
|
}
|
|
|
|
/* The prefix was determined. */
|
|
grub_sprintf (stage2, "%s%s", prefix, "/stage2");
|
|
grub_sprintf (config_filename, "%s%s", prefix, "/menu.lst");
|
|
*real_config_filename = 0;
|
|
|
|
/* Check if stage2 exists. */
|
|
if (! check_file (stage2))
|
|
goto fail;
|
|
|
|
{
|
|
char *fsys = fsys_table[fsys_type].name;
|
|
int i;
|
|
int size = sizeof (stage1_5_map) / sizeof (stage1_5_map[0]);
|
|
|
|
/* Iterate finding the same filesystem name as FSYS. */
|
|
for (i = 0; i < size; i++)
|
|
if (grub_strcmp (fsys, stage1_5_map[i].fsys) == 0)
|
|
{
|
|
/* OK, check if the Stage 1.5 exists. */
|
|
char stage1_5[64];
|
|
|
|
grub_sprintf (stage1_5, "%s%s", prefix, stage1_5_map[i].name);
|
|
if (check_file (stage1_5))
|
|
{
|
|
if (embed_stage1_5 (stage1_5,
|
|
installed_drive, installed_partition)
|
|
|| embed_stage1_5 (stage1_5,
|
|
image_drive, image_partition))
|
|
{
|
|
grub_strcpy (real_config_filename, config_filename);
|
|
sprint_device (image_drive, image_partition);
|
|
grub_sprintf (config_filename, "%s%s", device, stage2);
|
|
grub_strcpy (stage2, buffer);
|
|
}
|
|
}
|
|
errnum = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Construct a string that is used by the command "install" as its
|
|
arguments. */
|
|
sprint_device (installed_drive, installed_partition);
|
|
|
|
#if 1
|
|
/* Don't embed a drive number unnecessarily. */
|
|
grub_sprintf (cmd_arg, "%s%s%s%s %s%s %s p %s %s",
|
|
is_force_lba? "--force-lba " : "",
|
|
stage2_arg? stage2_arg : "",
|
|
stage2_arg? " " : "",
|
|
stage1,
|
|
(installed_drive != image_drive) ? "d " : "",
|
|
device,
|
|
stage2,
|
|
config_filename,
|
|
real_config_filename);
|
|
#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. */
|
|
grub_sprintf (cmd_arg, "%s%s%s%s d %s %s p %s %s",
|
|
is_force_lba? "--force-lba " : "",
|
|
stage2_arg? stage2_arg : "",
|
|
stage2_arg? " " : "",
|
|
stage1,
|
|
device,
|
|
stage2,
|
|
config_filename,
|
|
real_config_filename);
|
|
#endif /* NOT USED */
|
|
|
|
/* Notify what will be run. */
|
|
grub_printf (" Running \"install %s\"... ", cmd_arg);
|
|
|
|
/* Make sure that SAVED_DRIVE and SAVED_PARTITION are identical
|
|
with IMAGE_DRIVE and IMAGE_PARTITION, respectively. */
|
|
saved_drive = image_drive;
|
|
saved_partition = image_partition;
|
|
|
|
/* Run the command. */
|
|
if (! install_func (cmd_arg, flags))
|
|
grub_printf ("succeeded\nDone.\n");
|
|
else
|
|
grub_printf ("failed\n");
|
|
|
|
fail:
|
|
saved_drive = tmp_drive;
|
|
saved_partition = tmp_partition;
|
|
return errnum;
|
|
}
|
|
|
|
static struct builtin builtin_setup =
|
|
{
|
|
"setup",
|
|
setup_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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."
|
|
};
|
|
|
|
|
|
#if defined(SUPPORT_SERIAL) || defined(SUPPORT_HERCULES)
|
|
/* terminal */
|
|
static int
|
|
terminal_func (char *arg, int flags)
|
|
{
|
|
/* The index of the default terminal in TERM_TABLE. */
|
|
int default_term = -1;
|
|
struct term_entry *prev_term = current_term;
|
|
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)
|
|
{
|
|
if (grub_memcmp (arg, "--dumb", sizeof ("--dumb") - 1) == 0)
|
|
term_flags |= TERM_DUMB;
|
|
else if (grub_memcmp (arg, "--no-echo", sizeof ("--no-echo") - 1) == 0)
|
|
/* ``--no-echo'' implies ``--no-edit''. */
|
|
term_flags |= (TERM_NO_ECHO | TERM_NO_EDIT);
|
|
else if (grub_memcmp (arg, "--no-edit", sizeof ("--no-edit") - 1) == 0)
|
|
term_flags |= TERM_NO_EDIT;
|
|
else if (grub_memcmp (arg, "--timeout=", sizeof ("--timeout=") - 1) == 0)
|
|
{
|
|
char *val = arg + sizeof ("--timeout=") - 1;
|
|
|
|
if (! safe_parse_maxint (&val, &to))
|
|
return 1;
|
|
}
|
|
else if (grub_memcmp (arg, "--lines=", sizeof ("--lines=") - 1) == 0)
|
|
{
|
|
char *val = arg + sizeof ("--lines=") - 1;
|
|
|
|
if (! safe_parse_maxint (&val, &lines))
|
|
return 1;
|
|
|
|
/* Probably less than four is meaningless.... */
|
|
if (lines < 4)
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
}
|
|
else if (grub_memcmp (arg, "--silent", sizeof ("--silent") - 1) == 0)
|
|
no_message = 1;
|
|
else
|
|
break;
|
|
|
|
arg = skip_to (0, arg);
|
|
}
|
|
|
|
/* If no argument is specified, show current setting. */
|
|
if (! *arg)
|
|
{
|
|
grub_printf ("%s%s%s%s\n",
|
|
current_term->name,
|
|
current_term->flags & TERM_DUMB ? " (dumb)" : "",
|
|
current_term->flags & TERM_NO_EDIT ? " (no edit)" : "",
|
|
current_term->flags & TERM_NO_ECHO ? " (no echo)" : "");
|
|
return 0;
|
|
}
|
|
|
|
while (*arg)
|
|
{
|
|
int i;
|
|
char *next = skip_to (0, arg);
|
|
|
|
nul_terminate (arg);
|
|
|
|
for (i = 0; term_table[i].name; i++)
|
|
{
|
|
if (grub_strcmp (arg, term_table[i].name) == 0)
|
|
{
|
|
if (term_table[i].flags & TERM_NEED_INIT)
|
|
{
|
|
errnum = ERR_DEV_NEED_INIT;
|
|
return 1;
|
|
}
|
|
|
|
if (default_term < 0)
|
|
default_term = i;
|
|
|
|
term_bitmap |= (1 << i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (! term_table[i].name)
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
|
|
arg = next;
|
|
}
|
|
|
|
/* If multiple terminals are specified, wait until the user pushes any
|
|
key on one of the terminals. */
|
|
if (term_bitmap & ~(1 << default_term))
|
|
{
|
|
int time1, time2 = -1;
|
|
|
|
/* XXX: Disable the pager. */
|
|
count_lines = -1;
|
|
|
|
/* Get current time. */
|
|
while ((time1 = getrtsecs ()) == 0xFF)
|
|
;
|
|
|
|
/* 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 ((time1 = getrtsecs ()) != time2 && time1 != 0xFF)
|
|
{
|
|
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. */
|
|
current_term = prev_term;
|
|
}
|
|
|
|
time2 = time1;
|
|
if (to > 0)
|
|
to--;
|
|
}
|
|
}
|
|
}
|
|
|
|
end:
|
|
current_term = term_table + default_term;
|
|
current_term->flags = term_flags;
|
|
|
|
if (lines)
|
|
max_lines = lines;
|
|
else
|
|
/* 24 would be a good default value. */
|
|
max_lines = 24;
|
|
|
|
/* If the interface is currently the command-line,
|
|
restart it to repaint the screen. */
|
|
if (current_term != prev_term && (flags & BUILTIN_CMDLINE))
|
|
grub_longjmp (restart_cmdline_env, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_terminal =
|
|
{
|
|
"terminal",
|
|
terminal_func,
|
|
BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"terminal [--dumb] [--no-echo] [--no-edit] [--timeout=SECS] [--lines=LINES] [--silent] [console] [serial] [hercules]",
|
|
"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 */
|
|
|
|
|
|
#ifdef SUPPORT_SERIAL
|
|
static int
|
|
terminfo_func (char *arg, int flags)
|
|
{
|
|
struct terminfo term;
|
|
|
|
if (*arg)
|
|
{
|
|
struct
|
|
{
|
|
const char *name;
|
|
char *var;
|
|
}
|
|
options[] =
|
|
{
|
|
{"--name=", term.name},
|
|
{"--cursor-address=", term.cursor_address},
|
|
{"--clear-screen=", term.clear_screen},
|
|
{"--enter-standout-mode=", term.enter_standout_mode},
|
|
{"--exit-standout-mode=", term.exit_standout_mode}
|
|
};
|
|
|
|
grub_memset (&term, 0, sizeof (term));
|
|
|
|
while (*arg)
|
|
{
|
|
int i;
|
|
char *next = skip_to (0, arg);
|
|
|
|
nul_terminate (arg);
|
|
|
|
for (i = 0; i < sizeof (options) / sizeof (options[0]); i++)
|
|
{
|
|
const char *name = options[i].name;
|
|
int len = grub_strlen (name);
|
|
|
|
if (! grub_memcmp (arg, name, len))
|
|
{
|
|
grub_strcpy (options[i].var, ti_unescape_string (arg + len));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == sizeof (options) / sizeof (options[0]))
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return errnum;
|
|
}
|
|
|
|
arg = next;
|
|
}
|
|
|
|
if (term.name[0] == 0 || term.cursor_address[0] == 0)
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return errnum;
|
|
}
|
|
|
|
ti_set_term (&term);
|
|
}
|
|
else
|
|
{
|
|
/* No option specifies printing out current settings. */
|
|
ti_get_term (&term);
|
|
|
|
grub_printf ("name=%s\n",
|
|
ti_escape_string (term.name));
|
|
grub_printf ("cursor_address=%s\n",
|
|
ti_escape_string (term.cursor_address));
|
|
grub_printf ("clear_screen=%s\n",
|
|
ti_escape_string (term.clear_screen));
|
|
grub_printf ("enter_standout_mode=%s\n",
|
|
ti_escape_string (term.enter_standout_mode));
|
|
grub_printf ("exit_standout_mode=%s\n",
|
|
ti_escape_string (term.exit_standout_mode));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_terminfo =
|
|
{
|
|
"terminfo",
|
|
terminfo_func,
|
|
BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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
|
|
testload_func (char *arg, int flags)
|
|
{
|
|
int i;
|
|
|
|
kernel_type = KERNEL_TYPE_NONE;
|
|
|
|
if (! grub_open (arg))
|
|
return 1;
|
|
|
|
disk_read_hook = disk_read_print_func;
|
|
|
|
/* Perform filesystem test on the specified file. */
|
|
/* Read whole file first. */
|
|
grub_printf ("Whole file: ");
|
|
|
|
grub_read ((char *) RAW_ADDR (0x100000), -1);
|
|
|
|
/* Now compare two sections of the file read differently. */
|
|
|
|
for (i = 0; i < 0x10ac0; i++)
|
|
{
|
|
*((unsigned char *) RAW_ADDR (0x200000 + i)) = 0;
|
|
*((unsigned char *) RAW_ADDR (0x300000 + i)) = 1;
|
|
}
|
|
|
|
/* First partial read. */
|
|
grub_printf ("\nPartial read 1: ");
|
|
|
|
grub_seek (0);
|
|
grub_read ((char *) RAW_ADDR (0x200000), 0x7);
|
|
grub_read ((char *) RAW_ADDR (0x200007), 0x100);
|
|
grub_read ((char *) RAW_ADDR (0x200107), 0x10);
|
|
grub_read ((char *) RAW_ADDR (0x200117), 0x999);
|
|
grub_read ((char *) RAW_ADDR (0x200ab0), 0x10);
|
|
grub_read ((char *) RAW_ADDR (0x200ac0), 0x10000);
|
|
|
|
/* Second partial read. */
|
|
grub_printf ("\nPartial read 2: ");
|
|
|
|
grub_seek (0);
|
|
grub_read ((char *) RAW_ADDR (0x300000), 0x10000);
|
|
grub_read ((char *) RAW_ADDR (0x310000), 0x10);
|
|
grub_read ((char *) RAW_ADDR (0x310010), 0x7);
|
|
grub_read ((char *) RAW_ADDR (0x310017), 0x10);
|
|
grub_read ((char *) RAW_ADDR (0x310027), 0x999);
|
|
grub_read ((char *) RAW_ADDR (0x3109c0), 0x100);
|
|
|
|
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;
|
|
|
|
grub_printf ("Max is 0x10ac0: i=0x%x, filepos=0x%x\n", i, filepos);
|
|
disk_read_hook = 0;
|
|
grub_close ();
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_testload =
|
|
{
|
|
"testload",
|
|
testload_func,
|
|
BUILTIN_CMDLINE,
|
|
"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
|
|
testvbe_func (char *arg, int flags)
|
|
{
|
|
int mode_number;
|
|
struct vbe_controller controller;
|
|
struct vbe_mode mode;
|
|
|
|
if (! *arg)
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
|
|
if (! safe_parse_maxint (&arg, &mode_number))
|
|
return 1;
|
|
|
|
/* Preset `VBE2'. */
|
|
grub_memmove (controller.signature, "VBE2", 4);
|
|
|
|
/* Detect VBE BIOS. */
|
|
if (get_vbe_controller_info (&controller) != 0x004F)
|
|
{
|
|
grub_printf (" VBE BIOS is not present.\n");
|
|
return 0;
|
|
}
|
|
|
|
if (controller.version < 0x0200)
|
|
{
|
|
grub_printf (" VBE version %d.%d is not supported.\n",
|
|
(int) (controller.version >> 8),
|
|
(int) (controller.version & 0xFF));
|
|
return 0;
|
|
}
|
|
|
|
if (get_vbe_mode_info (mode_number, &mode) != 0x004F
|
|
|| (mode.mode_attributes & 0x0091) != 0x0091)
|
|
{
|
|
grub_printf (" Mode 0x%x is not supported.\n", mode_number);
|
|
return 0;
|
|
}
|
|
|
|
/* Now trip to the graphics mode. */
|
|
if (set_vbe_mode (mode_number | (1 << 14)) != 0x004F)
|
|
{
|
|
grub_printf (" Switching to Mode 0x%x failed.\n", mode_number);
|
|
return 0;
|
|
}
|
|
|
|
/* Draw something on the screen... */
|
|
{
|
|
unsigned char *base_buf = (unsigned char *) mode.phys_base;
|
|
int scanline = controller.version >= 0x0300
|
|
? mode.linear_bytes_per_scanline : mode.bytes_per_scanline;
|
|
/* FIXME: this assumes that any depth is a modulo of 8. */
|
|
int bpp = mode.bits_per_pixel / 8;
|
|
int width = mode.x_resolution;
|
|
int height = mode.y_resolution;
|
|
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++)
|
|
{
|
|
unsigned char *line_buf = base_buf + scanline * y;
|
|
|
|
for (x = 0; x < width; x++)
|
|
{
|
|
unsigned char *buf = line_buf + bpp * x;
|
|
int i;
|
|
|
|
for (i = 0; i < bpp; i++, buf++)
|
|
*buf = (color >> (i * 8)) & 0xff;
|
|
}
|
|
|
|
color++;
|
|
}
|
|
}
|
|
|
|
/* Discard the input. */
|
|
getkey ();
|
|
}
|
|
|
|
/* Back to the default text mode. */
|
|
if (set_vbe_mode (0x03) != 0x004F)
|
|
{
|
|
/* Why?! */
|
|
grub_reboot ();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_testvbe =
|
|
{
|
|
"testvbe",
|
|
testvbe_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"testvbe MODE",
|
|
"Test the VBE mode MODE. Hit any key to return."
|
|
};
|
|
|
|
|
|
#ifdef SUPPORT_NETBOOT
|
|
/* tftpserver */
|
|
static int
|
|
tftpserver_func (char *arg, int flags)
|
|
{
|
|
if (! *arg || ! ifconfig (0, 0, 0, arg))
|
|
{
|
|
errnum = ERR_BAD_ARGUMENT;
|
|
return 1;
|
|
}
|
|
|
|
print_network_configuration ();
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_tftpserver =
|
|
{
|
|
"tftpserver",
|
|
tftpserver_func,
|
|
BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
|
|
"tftpserver IPADDR",
|
|
"Override the TFTP server address."
|
|
};
|
|
#endif /* SUPPORT_NETBOOT */
|
|
|
|
|
|
/* timeout */
|
|
static int
|
|
timeout_func (char *arg, int flags)
|
|
{
|
|
if (! safe_parse_maxint (&arg, &grub_timeout))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_timeout =
|
|
{
|
|
"timeout",
|
|
timeout_func,
|
|
BUILTIN_MENU,
|
|
#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
|
|
title_func (char *arg, int flags)
|
|
{
|
|
/* This function is not actually used at least currently. */
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_title =
|
|
{
|
|
"title",
|
|
title_func,
|
|
BUILTIN_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
|
|
unhide_func (char *arg, int flags)
|
|
{
|
|
if (! set_device (arg))
|
|
return 1;
|
|
|
|
if (! set_partition_hidden_flag (0))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_unhide =
|
|
{
|
|
"unhide",
|
|
unhide_func,
|
|
BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
|
|
"unhide PARTITION",
|
|
"Unhide PARTITION by clearing the \"hidden\" bit in its"
|
|
" partition type code."
|
|
};
|
|
|
|
|
|
/* uppermem */
|
|
static int
|
|
uppermem_func (char *arg, int flags)
|
|
{
|
|
if (! safe_parse_maxint (&arg, (int *) &mbi.mem_upper))
|
|
return 1;
|
|
|
|
mbi.flags &= ~MB_INFO_MEM_MAP;
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_uppermem =
|
|
{
|
|
"uppermem",
|
|
uppermem_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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
|
|
vbeprobe_func (char *arg, int flags)
|
|
{
|
|
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)
|
|
{
|
|
unsigned short seg = (ptr >> 16);
|
|
unsigned short off = (ptr & 0xFFFF);
|
|
|
|
return (seg << 4) + off;
|
|
}
|
|
|
|
if (*arg)
|
|
{
|
|
if (! safe_parse_maxint (&arg, &mode_number))
|
|
return 1;
|
|
}
|
|
|
|
/* Set the signature to `VBE2', to obtain VBE 3.0 information. */
|
|
grub_memmove (controller.signature, "VBE2", 4);
|
|
|
|
if (get_vbe_controller_info (&controller) != 0x004F)
|
|
{
|
|
grub_printf (" VBE BIOS is not present.\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Check the version. */
|
|
if (controller.version < 0x0200)
|
|
{
|
|
grub_printf (" VBE version %d.%d is not supported.\n",
|
|
(int) (controller.version >> 8),
|
|
(int) (controller.version & 0xFF));
|
|
return 0;
|
|
}
|
|
|
|
/* Print some information. */
|
|
grub_printf (" VBE version %d.%d\n",
|
|
(int) (controller.version >> 8),
|
|
(int) (controller.version & 0xFF));
|
|
|
|
/* Iterate probing modes. */
|
|
for (mode_list
|
|
= (unsigned short *) vbe_far_ptr_to_linear (controller.video_mode);
|
|
*mode_list != 0xFFFF;
|
|
mode_list++)
|
|
{
|
|
struct vbe_mode mode;
|
|
|
|
if (get_vbe_mode_info (*mode_list, &mode) != 0x004F)
|
|
continue;
|
|
|
|
/* Skip this, if this is not supported or linear frame buffer
|
|
mode is not support. */
|
|
if ((mode.mode_attributes & 0x0081) != 0x0081)
|
|
continue;
|
|
|
|
if (mode_number == -1 || mode_number == *mode_list)
|
|
{
|
|
char *model;
|
|
switch (mode.memory_model)
|
|
{
|
|
case 0x00: model = "Text"; break;
|
|
case 0x01: model = "CGA graphics"; break;
|
|
case 0x02: model = "Hercules graphics"; break;
|
|
case 0x03: model = "Planar"; break;
|
|
case 0x04: model = "Packed pixel"; break;
|
|
case 0x05: model = "Non-chain 4, 256 color"; break;
|
|
case 0x06: model = "Direct Color"; break;
|
|
case 0x07: model = "YUV"; break;
|
|
default: model = "Unknown"; break;
|
|
}
|
|
|
|
grub_printf (" 0x%x: %s, %ux%ux%u\n",
|
|
(unsigned) *mode_list,
|
|
model,
|
|
(unsigned) mode.x_resolution,
|
|
(unsigned) mode.y_resolution,
|
|
(unsigned) mode.bits_per_pixel);
|
|
|
|
if (mode_number != -1)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mode_number != -1 && mode_number != *mode_list)
|
|
grub_printf (" Mode 0x%x is not found or supported.\n", mode_number);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct builtin builtin_vbeprobe =
|
|
{
|
|
"vbeprobe",
|
|
vbeprobe_func,
|
|
BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
|
|
"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[] =
|
|
{
|
|
&builtin_blocklist,
|
|
&builtin_boot,
|
|
#ifdef SUPPORT_NETBOOT
|
|
&builtin_bootp,
|
|
#endif /* SUPPORT_NETBOOT */
|
|
&builtin_cat,
|
|
&builtin_chainloader,
|
|
&builtin_cmp,
|
|
&builtin_color,
|
|
&builtin_configfile,
|
|
&builtin_debug,
|
|
&builtin_default,
|
|
#ifdef GRUB_UTIL
|
|
&builtin_device,
|
|
#endif /* GRUB_UTIL */
|
|
#ifdef SUPPORT_NETBOOT
|
|
&builtin_dhcp,
|
|
#endif /* SUPPORT_NETBOOT */
|
|
&builtin_displayapm,
|
|
&builtin_displaymem,
|
|
#ifdef GRUB_UTIL
|
|
&builtin_dump,
|
|
#endif /* GRUB_UTIL */
|
|
&builtin_embed,
|
|
&builtin_fallback,
|
|
&builtin_find,
|
|
&builtin_fstest,
|
|
&builtin_geometry,
|
|
&builtin_halt,
|
|
&builtin_help,
|
|
&builtin_hiddenmenu,
|
|
&builtin_hide,
|
|
#ifdef SUPPORT_NETBOOT
|
|
&builtin_ifconfig,
|
|
#endif /* SUPPORT_NETBOOT */
|
|
&builtin_impsprobe,
|
|
&builtin_initrd,
|
|
&builtin_install,
|
|
&builtin_ioprobe,
|
|
&builtin_kernel,
|
|
&builtin_lock,
|
|
&builtin_makeactive,
|
|
&builtin_map,
|
|
#ifdef USE_MD5_PASSWORDS
|
|
&builtin_md5crypt,
|
|
#endif /* USE_MD5_PASSWORDS */
|
|
&builtin_module,
|
|
&builtin_modulenounzip,
|
|
&builtin_pager,
|
|
&builtin_partnew,
|
|
&builtin_parttype,
|
|
&builtin_password,
|
|
&builtin_pause,
|
|
#ifdef GRUB_UTIL
|
|
&builtin_quit,
|
|
#endif /* GRUB_UTIL */
|
|
#ifdef SUPPORT_NETBOOT
|
|
&builtin_rarp,
|
|
#endif /* SUPPORT_NETBOOT */
|
|
&builtin_read,
|
|
&builtin_reboot,
|
|
&builtin_root,
|
|
&builtin_rootnoverify,
|
|
&builtin_savedefault,
|
|
#ifdef SUPPORT_SERIAL
|
|
&builtin_serial,
|
|
#endif /* SUPPORT_SERIAL */
|
|
&builtin_setkey,
|
|
&builtin_setup,
|
|
#if defined(SUPPORT_SERIAL) || defined(SUPPORT_HERCULES)
|
|
&builtin_terminal,
|
|
#endif /* SUPPORT_SERIAL || SUPPORT_HERCULES */
|
|
#ifdef SUPPORT_SERIAL
|
|
&builtin_terminfo,
|
|
#endif /* SUPPORT_SERIAL */
|
|
&builtin_testload,
|
|
&builtin_testvbe,
|
|
#ifdef SUPPORT_NETBOOT
|
|
&builtin_tftpserver,
|
|
#endif /* SUPPORT_NETBOOT */
|
|
&builtin_timeout,
|
|
&builtin_title,
|
|
&builtin_unhide,
|
|
&builtin_uppermem,
|
|
&builtin_vbeprobe,
|
|
0
|
|
};
|